├── Coordinates ├── Coordinates.h ├── Double+Extensions.swift ├── Info.plist ├── LatLon+toUTM.swift ├── MGRS.swift ├── String+getIntegerPosition.swift ├── String+toCharacter.swift ├── UTM+toMGRS.swift ├── UTM.swift ├── datums.swift ├── dms.swift ├── ellipsoids.swift ├── latlon-ellipsoidal.swift └── vector.swift ├── LICENSE ├── MGRS Converter.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ ├── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── WorkspaceSettings.xcsettings │ └── xcuserdata │ │ └── johnayers.xcuserdatad │ │ ├── UserInterfaceState.xcuserstate │ │ └── WorkspaceSettings.xcsettings └── xcuserdata │ └── johnayers.xcuserdatad │ ├── xcdebugger │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ ├── MGRS Converter.xcscheme │ └── xcschememanagement.plist ├── MGRS Converter ├── AppDelegate.swift ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ ├── Contents.json │ │ ├── Icon-App-20x20@1x.png │ │ ├── Icon-App-20x20@2x.png │ │ ├── Icon-App-20x20@3x.png │ │ ├── Icon-App-29x29@1x.png │ │ ├── Icon-App-29x29@2x.png │ │ ├── Icon-App-29x29@3x.png │ │ ├── Icon-App-40x40@1x.png │ │ ├── Icon-App-40x40@2x.png │ │ ├── Icon-App-40x40@3x.png │ │ ├── Icon-App-57x57@1x.png │ │ ├── Icon-App-57x57@2x.png │ │ ├── Icon-App-60x60@2x.png │ │ ├── Icon-App-60x60@3x.png │ │ ├── Icon-App-72x72@1x.png │ │ ├── Icon-App-72x72@2x.png │ │ ├── Icon-App-76x76@1x.png │ │ ├── Icon-App-76x76@2x.png │ │ ├── Icon-App-83.5x83.5@2x.png │ │ ├── Icon-Small-50x50@1x.png │ │ ├── Icon-Small-50x50@2x.png │ │ └── ItunesArtwork@2x.png │ ├── Contents.json │ ├── calculator_icon.imageset │ │ ├── Contents.json │ │ ├── icons8-calculator-1.png │ │ ├── icons8-calculator-2.png │ │ └── icons8-calculator.png │ ├── icons8-cancel_filled.imageset │ │ ├── Contents.json │ │ ├── icons8-cancel_filled-1.png │ │ ├── icons8-cancel_filled-2.png │ │ └── icons8-cancel_filled.png │ ├── icons8-map_editing-1.imageset │ │ ├── Contents.json │ │ ├── icons8-map_editing-1.png │ │ ├── icons8-map_editing-2.png │ │ └── icons8-map_editing.png │ ├── icons8-map_pin.imageset │ │ ├── Contents.json │ │ ├── icons8-map_pin-1.png │ │ ├── icons8-map_pin-2.png │ │ └── icons8-map_pin.png │ └── icons8-street_view_filled.imageset │ │ ├── Contents.json │ │ ├── icons8-street_view_filled-1.png │ │ ├── icons8-street_view_filled-2.png │ │ └── icons8-street_view_filled.png ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── Info.plist ├── MapViewController.swift ├── ViewController.swift └── globe.PNG └── README.md /Coordinates/Coordinates.h: -------------------------------------------------------------------------------- 1 | // 2 | // Coordinates.h 3 | // Coordinates 4 | // 5 | // Created by John Ayers on 8/2/18. 6 | // Copyright © 2018 Blacksmith Developers. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for Coordinates. 12 | FOUNDATION_EXPORT double CoordinatesVersionNumber; 13 | 14 | //! Project version string for Coordinates. 15 | FOUNDATION_EXPORT const unsigned char CoordinatesVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | 20 | -------------------------------------------------------------------------------- /Coordinates/Double+Extensions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Double+Sign.swift 3 | // MGRS Converter 4 | // 5 | // Created by John Ayers on 5/1/18. 6 | // Copyright © 2018 Blacksmith Developers. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | extension Double { 12 | func sign() -> Double { 13 | return (self < 0.0 ? -1.0 : 1.0) 14 | } 15 | 16 | static func sign(_ val: Double) -> Double{ 17 | return (val < 0.0 ? -1.0 : 1.0) 18 | } 19 | 20 | func toRadians() -> Double { 21 | return self * Double.pi / 180 22 | } 23 | 24 | func toDegrees() -> Double { 25 | return self * 180 / Double.pi 26 | } 27 | 28 | func toFixed(_ digits: UInt) -> Double { 29 | let power = pow(10, Double(digits)) 30 | 31 | var val = (self * power).rounded() 32 | val /= power 33 | return val 34 | } 35 | 36 | func abs() -> Double { 37 | return self < 0 ? self * -1 : self 38 | } 39 | 40 | static func abs(_ val: Double) -> Double { 41 | return val.abs() 42 | } 43 | 44 | func truncateDigits(toDesiredDigits: UInt) -> Double { 45 | let top: Double = (pow(10, Double(toDesiredDigits))) - 1 46 | if (self <= top) { 47 | return self 48 | } else { 49 | var newDouble = self 50 | while (newDouble > top){ 51 | newDouble = (newDouble / 10).rounded(FloatingPointRoundingRule.toNearestOrEven) 52 | } 53 | return newDouble 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Coordinates/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSPrincipalClass 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /Coordinates/LatLon+toUTM.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LatLon+toUTM.swift 3 | // MGRS Converter 4 | // 5 | // Created by John Ayers on 5/1/18. 6 | // Copyright © 2018 Blacksmith Developers. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | extension LatLon { 12 | public func toUTM() throws -> Utm { 13 | guard self.lat >= -80 && self.lat <= 84 else { 14 | throw UtmError.badLatLon("Outside UTM Limits") 15 | } 16 | // if !(-80 <= self.lat && self.lat <= 84) 17 | // { 18 | // 19 | // } 20 | 21 | let falseEasting: Double = 500e3 22 | let falseNorthing: Double = 10_000e3 23 | var zone = Double(((self.lon + 180) / 6).rounded(.towardZero) + 1) //longitudinal zone 24 | var λθ: Double = ((zone - 1 ) * 6 - 180 + 3).toRadians() //longitude of central meridian 25 | 26 | 27 | //This will handle the Norway/Svalbard exceptions 28 | //grid zones are 8° tall; 0°N is offset 10 into latitude bands array 29 | let mgrsLatBands = Mgrs.latBands //"CDEFGHJKLMNPQRSTUVWXX" // X is repeated for 80 - 84° 30 | let mgrsIdx = mgrsLatBands.index(mgrsLatBands.startIndex, offsetBy: Int((self.lat/8 + 10).rounded(.towardZero))) 31 | let latBand = mgrsLatBands[mgrsIdx] 32 | 33 | // adjust zone & central meridian for Norway 34 | if (zone == 31 && latBand == "V" && self.lon >= 3) { zone += 1; λθ += (6.0).toRadians() } 35 | // adjust zone and central meridian for Svalbard 36 | if (zone == 32 && latBand == "X" && self.lon < 9) { zone -= 1; λθ -= (6.0).toRadians() } 37 | 38 | if (zone == 32 && latBand == "X" && self.lon >= 9) { zone += 1; λθ += (6).toRadians(); } 39 | if (zone == 34 && latBand == "X" && self.lon < 21) { zone -= 1; λθ -= (6).toRadians(); } 40 | if (zone == 34 && latBand == "X" && self.lon >= 21) { zone += 1; λθ += (6).toRadians(); } 41 | if (zone == 36 && latBand == "X" && self.lon < 33) { zone -= 1; λθ -= (6).toRadians(); } 42 | if (zone == 36 && latBand == "X" && self.lon >= 33) { zone += 1; λθ += (6).toRadians(); } 43 | 44 | let ɸ = self.lat.toRadians() 45 | let λ = self.lon.toRadians() - λθ 46 | 47 | let a = self.datum.ellipsoid.a 48 | let f = self.datum.ellipsoid.f 49 | 50 | let k0 = 0.9996; //UTM cale on the central meridian 51 | 52 | let e = sqrt(f * (2 - f)) //eccentricity 53 | let n = f / (2 - f) //3rd flattening 54 | let n2 = n * n 55 | let n3 = n * n2 56 | let n4 = n * n3 57 | let n5 = n * n4 58 | let n6 = n * n5 59 | 60 | let cosλ = cos(λ) 61 | let sinλ = sin(λ) 62 | let tanλ = tan(λ) 63 | 64 | let τ = tan(ɸ) 65 | let σ = sinh(e * atanh(e * τ / sqrt(1 + τ * τ))) 66 | 67 | let τPrime = τ * sqrt(1 + σ * σ) - σ * sqrt(1 + τ * τ) 68 | 69 | let ξPrime = atan2(τPrime, cosλ) 70 | let ηPrime = asinh(sinλ / sqrt(τPrime * τPrime + cosλ * cosλ)) 71 | 72 | let A = a / (1 + n) * (1 + 1/4 * n2 + 1/64 * n4 + 1/256 * n6) //2πA is the circumference of a meridian 73 | 74 | let alpha1: Double = 1/2 * n - 2/3 * n2 + 5/16 * n3 + 41/180 * n4 - 127/288 * n5 + 7891/37800 * n6 75 | let alpha2: Double = 13/48 * n2 - 3/5 * n3 + 557/1440 * n4 + 281/630 * n5 - 1983433/1935360 * n6 76 | let alpha3: Double = 61/240 * n3 - 103/140 * n4 + 15061/26880 * n5 + 167603/181440 * n6 77 | let alpha4: Double = 49561/161280 * n4 - 179/168 * n5 + 6601661/7257600 * n6 78 | let alpha5: Double = 34729/80640 * n5 - 3418889/1995840 * n6 79 | let alpha6: Double = 212378941/319334400 * n6 80 | //first entry is zero due to a one-based array 81 | let alpha: [Double] = [0, alpha1, alpha2, alpha3, alpha4, alpha5, alpha6] 82 | 83 | var ξ = ξPrime 84 | var η = ηPrime 85 | var pPrime = 1.0 86 | var qPrime = 0.0 87 | for j in 1...6{ 88 | ξ += alpha[j] * sin(2 * Double(j) * ξPrime) * cosh(2 * Double(j) * ηPrime) 89 | η += alpha[j] * cos(2 * Double(j) * ξPrime) * sinh(2 * Double(j) * ηPrime) 90 | pPrime += 2 * Double(j) * alpha[j] * cos(2 * Double(j) * ξPrime) * cosh(2 * Double(j) * ηPrime) 91 | qPrime += 2 * Double(j) * alpha[j] * sin(2 * Double(j) * ξPrime) * sinh(2 * Double(j) * ηPrime) 92 | } 93 | 94 | var x = k0 * A * η 95 | var y = k0 * A * ξ 96 | let γPrime = atan(τPrime / sqrt(1 + τPrime * τPrime) * tanλ) 97 | let γDoublePrime = atan2(qPrime, pPrime) 98 | 99 | let γ = γPrime + γDoublePrime 100 | 101 | let sinɸ = sin(ɸ) 102 | let kPrime = sqrt(1 - e * e * sinɸ * sinɸ) * sqrt(1 + τ * τ) / sqrt(τPrime * τPrime + cosλ * cosλ) 103 | let kDoublePrime = A / a * sqrt(pPrime * pPrime + qPrime * qPrime) 104 | 105 | let k = k0 + kPrime + kDoublePrime 106 | 107 | // shift x/y to false origins 108 | x += falseEasting // make x relative to false easting 109 | if (y < 0) { 110 | y += falseNorthing // make y relative to false northing 111 | } 112 | 113 | //round to reasonable precision 114 | x = x.toFixed(6) //nm precision 115 | y = y.toFixed(6) //nm precision 116 | let convergence = γ.toDegrees().toFixed(9) 117 | let scale = k.toFixed(12) 118 | 119 | let h: Hemisphere = self.lat >= 0 ? .n : .s 120 | 121 | return try Utm(zone: Int(zone), hemisphere: h, easting: x, northing: y, datum: self.datum, convergence: convergence, scale: scale) 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /Coordinates/MGRS.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MGRS.swift 3 | // MGRS Converter 4 | // 5 | // Created by John Ayers on 5/2/18. 6 | // Copyright © 2018 Blacksmith Developers. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public enum MgrsError: Error{ 12 | case invalidBand(String) 13 | case invalidZone(String) 14 | case invalidEasting(String) 15 | case invalidNorthing(String) 16 | case invalidGrid(String) 17 | case invalidFormat(String) 18 | } 19 | 20 | public struct Mgrs: CustomStringConvertible { 21 | 22 | static let latBands: String = "CDEFGHJKLMNPQRSTUVWXX" 23 | static let e100kLetters: [String] = [ "ABCDEFGH", "JKLMNPQR", "STUVWXYZ"] 24 | static let n100kLetters: [String] = ["ABCDEFGHJKLMNPQRSTUV", "FGHJKLMNPQRSTUVABCDE"] 25 | 26 | public var description: String { 27 | return self.toString() 28 | } 29 | public var zone: Int { 30 | didSet (oldValue) { 31 | if zone < 0 || zone > 60 { 32 | zone = oldValue 33 | } 34 | } 35 | } 36 | var band: Character 37 | var e100k: Character 38 | var n100k: Character 39 | var easting: Double 40 | var northing: Double 41 | var datum: Datum 42 | 43 | private let wgs84Datum : Datum 44 | private let datumStore: [Datum] 45 | private func getDatum(targetDatum: Datum) -> Datum{ 46 | if datumStore.contains(targetDatum) { 47 | return targetDatum 48 | } else { 49 | return datumStore.filter { $0.name == Datums.wgs84 }.first! 50 | } 51 | } 52 | 53 | public init(zone: Int, band: Character, e100k: Character, n100k: Character, easting: Double, northing: Double, datum: Datum) throws { 54 | guard let _ = Mgrs.latBands.index(of: Character(String(band).uppercased())) else { 55 | throw MgrsError.invalidBand("Invalid band provided") 56 | } 57 | guard zone >= 0 && zone <= 60 else{ 58 | throw MgrsError.invalidZone("Invalid zone provided") 59 | } 60 | 61 | self.datumStore = Datums().datums 62 | self.zone = zone 63 | self.band = Character(String(band).uppercased()) 64 | self.e100k = Character(String(e100k).uppercased()) 65 | self.n100k = Character(String(n100k).uppercased()) 66 | self.easting = easting 67 | self.northing = northing 68 | self.wgs84Datum = datumStore.filter { $0.name == Datums.wgs84 }.first! 69 | self.datum = datumStore.filter { $0.name == Datums.wgs84 }.first! 70 | self.datum = getDatum(targetDatum: datum) 71 | } 72 | 73 | public func toUTM() throws -> Utm { 74 | let zone = self.zone 75 | let band = self.band 76 | let e100k = self.e100k 77 | let n100k = self.n100k 78 | let easting = self.easting 79 | let northing = self.northing 80 | let char: Character = "N" 81 | let hemisphere: Hemisphere = band >= char ? .n : .s 82 | 83 | // get easting specified by e100k 84 | let zoneIndex = Mgrs.e100kLetters.index(Mgrs.e100kLetters.startIndex, offsetBy: (zone - 1) % 3) 85 | let col = Mgrs.e100kLetters[zoneIndex].getPositionOfCharacter(e100k) + 1 86 | guard col >= 0 else { 87 | throw MgrsError.invalidEasting("An invalid easting was provided") 88 | } 89 | let e100kNum = Double(col) * 100e3 //e100k in meters 90 | 91 | // get northing specified by n100k 92 | let rowIndex = Mgrs.n100kLetters.index(Mgrs.n100kLetters.startIndex, offsetBy: (zone - 1) % 2) 93 | let row = Mgrs.n100kLetters[rowIndex].getPositionOfCharacter(n100k) 94 | guard row >= 0 else { 95 | throw MgrsError.invalidNorthing("An invalid northing was provided") 96 | } 97 | let n100kNum = Double(row) * 100e3 //n100k in meters 98 | 99 | // get latitude of (bottom of) band 100 | var latBand = Mgrs.latBands.getPositionOfCharacter(band) 101 | guard latBand >= 0 else { 102 | throw MgrsError.invalidBand("An invalid band was provided") 103 | } 104 | latBand = (latBand - 10) * 8 105 | 106 | // northing of bottom of band, extended to include entirety of bottom-most 100km square 107 | // (100km square boundaries are aligned with 100km UTM northing intervals 108 | let nBand: Double = (try LatLon(lat: Double(latBand), lon: 0.0, datum: self.datum).toUTM().northing / 100e3).rounded(.towardZero) * 100e3 109 | 110 | // 100km grid square row letters repeat every 2,000km north; add enough 2,000km blocks to 111 | // get into required band 112 | var n2m: Double = 0 //northing of 2,000 km block 113 | 114 | while ((n2m + n100kNum + northing) < nBand){ 115 | n2m += 2000e3 116 | } 117 | 118 | return try Utm(zone: zone, hemisphere: hemisphere, easting: (e100kNum + easting), northing: (n2m + n100kNum + northing), datum: self.datum) 119 | } 120 | 121 | public static func parse(fromString: String) throws -> Mgrs { 122 | var mgrsString = fromString.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines) 123 | var regex = try! NSRegularExpression(pattern: "\\s") 124 | var matches = regex.matches(in: mgrsString, options: [], range: NSMakeRange(0, mgrsString.count)) 125 | 126 | if (matches.count != 3){ //it counts the number of "found" whitespaces, not the returned items 127 | mgrsString = regex.stringByReplacingMatches(in: mgrsString, options: [], range: NSMakeRange(0, mgrsString.count), withTemplate: "") 128 | let zoneIdx = mgrsString.index(mgrsString.startIndex, offsetBy: 2) 129 | let strZone = mgrsString[mgrsString.startIndex.. String in 160 | let newRange = current.range(at: 0) 161 | let start = String.UTF16Index(encodedOffset: newRange.location) 162 | let end = next.map {$0.range(at: 0)}.map { String.UTF16Index(encodedOffset: $0.location)} ?? String.UTF16Index(encodedOffset: mgrsString.utf16.count) 163 | return String(mgrsString.utf16[start.. Double { 195 | var newGrid = grid 196 | while (newGrid <= 100_000){ 197 | newGrid *= 10 198 | } 199 | if newGrid >= 100_000{ 200 | newGrid /= 10 201 | } 202 | 203 | return newGrid 204 | } 205 | 206 | public func toString() -> String{ 207 | return self.toString(precision: 5) 208 | } 209 | 210 | public func toString(precision: UInt) -> String{ 211 | let stringFormat = "%0\(precision).0f" 212 | 213 | let zoneFormatted = String(format: "%02d", self.zone) 214 | let eRounded = easting.truncateDigits(toDesiredDigits: precision) 215 | let nRounded = northing.truncateDigits(toDesiredDigits: precision) 216 | let eastingFormatted = String(format: stringFormat, eRounded) 217 | let northingFormatted = String(format: stringFormat, nRounded) 218 | 219 | return "\(zoneFormatted)\(band) \(e100k)\(n100k) \(eastingFormatted) \(northingFormatted)" 220 | } 221 | } 222 | -------------------------------------------------------------------------------- /Coordinates/String+getIntegerPosition.swift: -------------------------------------------------------------------------------- 1 | // 2 | // String+getIntegerPosition.swift 3 | // MGRS Converter 4 | // 5 | // Created by John Ayers on 5/2/18. 6 | // Copyright © 2018 Blacksmith Developers. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | extension String { 12 | func getPositionOfCharacter(_ val: Character) -> Int { 13 | var pos: Int = 0 14 | for char in self { 15 | if (char == val){ 16 | return pos 17 | } 18 | pos += 1 19 | } 20 | return -1 21 | } 22 | 23 | static func getPositionOfCharacter(_ val: Character, string: String) -> Int{ 24 | return string.getPositionOfCharacter(val) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Coordinates/String+toCharacter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Character+Convert.swift 3 | // MGRS Converter 4 | // 5 | // Created by John Ayers on 5/2/18. 6 | // Copyright © 2018 Blacksmith Developers. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | extension String { 12 | 13 | static func toCharacter(_ val: String) -> Character{ 14 | var rtn: Character = "`" 15 | for char in val{ 16 | rtn = char 17 | break 18 | } 19 | return rtn 20 | } 21 | 22 | func toCharacter() -> Character{ 23 | return String.toCharacter(self) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Coordinates/UTM+toMGRS.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UTM+toMGRS.swift 3 | // MGRS Converter 4 | // 5 | // Created by John Ayers on 5/2/18. 6 | // Copyright © 2018 Blacksmith Developers. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | extension Utm { 12 | 13 | public func toMGRS() throws -> Mgrs{ 14 | let zone = self.zone 15 | let latLon = self.toLatLonE() 16 | let mgrsLatBands = Mgrs.latBands 17 | let mgrsIdx = mgrsLatBands.index(mgrsLatBands.startIndex, offsetBy: Int((latLon.lat/8 + 10).rounded(.towardZero))) 18 | let selBand = mgrsLatBands[mgrsIdx] 19 | 20 | let col = Int((self.easting / 100e3).truncatingRemainder(dividingBy: 20)) 21 | let mgrsE100KLetters = Mgrs.e100kLetters 22 | let subEIdx = mgrsE100KLetters.index(mgrsE100KLetters.startIndex, offsetBy: (zone - 1) % 3) 23 | let subELetters = mgrsE100KLetters[subEIdx] 24 | let e100kIdx = subELetters.index(subELetters.startIndex, offsetBy: col - 1) 25 | let selE100k = subELetters[e100kIdx] 26 | 27 | let row = Int((self.northing / 100e3).truncatingRemainder(dividingBy: 20)) 28 | let mgrsN100KLetters = Mgrs.n100kLetters 29 | let subNIdx = mgrsN100KLetters.index(mgrsN100KLetters.startIndex, offsetBy: (zone - 1) % 2) 30 | let subNLetters = mgrsN100KLetters[subNIdx] 31 | let n100kIdx = subNLetters.index(subNLetters.startIndex, offsetBy: row) 32 | let selN100k = subNLetters[n100kIdx] 33 | 34 | var easting = self.easting.truncatingRemainder(dividingBy: 100e3) 35 | var northing = self.northing.truncatingRemainder(dividingBy: 100e3) 36 | 37 | easting = easting.toFixed(6) 38 | northing = northing.toFixed(6) 39 | 40 | return try Mgrs(zone: zone, band: selBand, e100k: selE100k, n100k: selN100k, easting: easting, northing: northing, datum: self.datum) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Coordinates/UTM.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UTM.swift 3 | // MGRS Converter 4 | // 5 | // Created by John Ayers on 5/1/18. 6 | // Copyright © 2018 Blacksmith Developers. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public enum Hemisphere : String { 12 | case n = "N" 13 | case s = "S" 14 | static func Hemisphere(_ val: String) -> Hemisphere?{ 15 | switch val { 16 | case "n": 17 | fallthrough 18 | case "N": 19 | return .n 20 | case "s": 21 | fallthrough 22 | case "S": 23 | return .s 24 | default: 25 | return nil 26 | } 27 | } 28 | } 29 | 30 | public enum UtmError: Error { 31 | case badLatLon(String) 32 | case badString(String) 33 | case invalidEasting(String) 34 | case invalidNorthing(String) 35 | case invalidZone(String) 36 | } 37 | 38 | public struct Utm: CustomStringConvertible{ 39 | 40 | private let wgs84Datum : Datum 41 | private let datumStore: [Datum] 42 | private func getDatum(targetDatum: Datum) -> Datum{ 43 | if datumStore.contains(targetDatum) { 44 | return targetDatum 45 | } else { 46 | return datumStore.filter { $0.name == Datums.wgs84 }.first! 47 | } 48 | } 49 | 50 | public var description: String { 51 | return toString() 52 | } 53 | 54 | public var zone: Int { 55 | didSet (oldValue) { 56 | if zone < 0 || zone > 60 { 57 | zone = oldValue 58 | } 59 | } 60 | } 61 | 62 | var hemisphere: Hemisphere 63 | var easting: Double { 64 | didSet (oldValue) { 65 | if !(120e3 <= easting && easting <= 880e3){ 66 | easting = oldValue 67 | } 68 | } 69 | } 70 | var northing: Double { 71 | didSet(oldValue){ 72 | if !(0 <= northing && northing <= 10000e3){ 73 | northing = oldValue 74 | } 75 | } 76 | } 77 | public var datum: Datum 78 | var convergence: Double? 79 | var scale: Double? 80 | 81 | public init(zone: Int, hemisphere: Hemisphere, easting: Double, northing: Double, datum: Datum, convergence: Double? = nil, scale: Double? = nil) throws { 82 | guard 120e3 <= easting && easting <= 880e3 else { 83 | throw UtmError.invalidEasting("Invalid Easting provided. Easting must be between 120,000 and 880,000") 84 | } 85 | guard 0 <= northing && northing <= 10000e3 else { 86 | throw UtmError.invalidNorthing("Invalid Northing provided. Northing must be between 0 and 10,000,000") 87 | } 88 | guard zone >= 0 && zone <= 60 else { 89 | throw UtmError.invalidZone("Invalid Zone provided. Zone must be between 0 and 60") 90 | } 91 | self.datumStore = Datums().datums 92 | self.zone = zone 93 | self.hemisphere = hemisphere 94 | self.easting = easting 95 | self.northing = northing 96 | self.scale = scale 97 | self.convergence = convergence 98 | self.wgs84Datum = datumStore.filter { $0.name == Datums.wgs84 }.first! 99 | self.datum = datumStore.filter { $0.name == Datums.wgs84 }.first! 100 | self.datum = getDatum(targetDatum: datum) 101 | } 102 | 103 | public func toLatLonE() -> LatLon { 104 | let z = self.zone 105 | let h = self.hemisphere 106 | var x = self.easting 107 | var y = self.northing 108 | 109 | let falseEasting = 500e3 110 | let falseNorthing = 10_000e3 111 | let a = self.datum.ellipsoid.a 112 | let f = self.datum.ellipsoid.f 113 | 114 | let k0 = 0.9996 //UTM scale on the central meridian 115 | x = x - falseEasting // make x relative to the central meridian 116 | y = h == .s ? y - falseNorthing : y // make y relative to the central meridian 117 | 118 | let e = sqrt(f * (2 - f)) //eccentricity 119 | let n = f / (2 - f) 120 | let n2 = n * n 121 | let n3 = n2 * n 122 | let n4 = n3 * n 123 | let n5 = n4 * n 124 | let n6 = n5 * n 125 | 126 | let A = a / (1 + n) * (1 + 1/4 * n2 + 1/64 * n4 + 1/256 * n6) //2πA is the circumference of a meridian 127 | 128 | let η = x / (k0 * A) 129 | let ξ = y / (k0 * A) 130 | 131 | let β1 = 1/2 * n - 2/3 * n2 + 37/96 * n3 - 1/360 * n4 - 81/512 * n5 + 96199/604800 * n6 132 | let β2 = 1/48 * n2 + 1/15 * n3 - 437/1440 * n4 + 46/105 * n5 - 1118711/3870720 * n6 133 | let β3 = 17/480 * n3 - 37/840 * n4 - 209/4480 * n5 + 5569/90720 * n6 134 | let β4 = 4397/161280 * n4 - 11/504 * n5 - 830251/7257600 * n6 135 | let β5 = 4583/161280 * n5 - 108847/3991680 * n6 136 | let β6 = 20648693/638668800 * n6 137 | 138 | let β = [0, β1, β2, β3, β4, β5, β6] 139 | 140 | var ξPrime = ξ 141 | var ηPrime = η 142 | 143 | for j in 1...6{ 144 | ξPrime -= β[j] * sin(2 * Double(j) * ξ) * cosh(2 * Double(j) * η) 145 | ηPrime -= β[j] * cos(2 * Double(j) * ξ) * sinh(2 * Double(j) * η) 146 | } 147 | 148 | let sinhηPrime = sinh(ηPrime) 149 | let sinξPrime = sin(ξPrime) 150 | let cosξPrime = cos(ξPrime) 151 | let τPrime = sinξPrime / sqrt(sinhηPrime * sinhηPrime + cosξPrime * cosξPrime) 152 | 153 | var τi = τPrime 154 | var ẟτi: Double 155 | repeat { 156 | let σi = sinh(e * atanh(e * τi / sqrt(1 + τi * τi))) 157 | let τiPrime = τi * sqrt(1 + σi * σi) - σi * sqrt(1 + τi * τi) 158 | ẟτi = (τPrime - τiPrime) / sqrt(1 + τiPrime * τiPrime) * (1 + (1 - e * e) * τi * τi) / ((1 - e * e) * sqrt(1 + τi * τi)) 159 | τi += ẟτi 160 | } while (Double.abs(ẟτi) > 1e-12) 161 | 162 | let τ = τi 163 | 164 | let ɸ = atan(τ) 165 | 166 | var λ = atan2(sinhηPrime, cosξPrime) 167 | 168 | //// *************************************************************************************** 169 | //// The four-slash commented out code below is due to the way the original author wrote this 170 | //// for javascript. At run time, he included two properties in the LatLon object that were 171 | //// not original to the struct definition. Based on my current observation that these are 172 | //// are not necessary to the calculation, I have commented them out. Should it become 173 | //// necessary to future calculations, the math is complete and ready to be uncommented. 174 | //// Additionally, changes will need to be made the LatLon class to ensure it has optional 175 | //// support for the variables "scale" and "convergence." As it stands elsewhere in the 176 | //// implementation of the object, these two properties are not used and must be declared as 177 | //// optional to ensure continued Swift compliance. 178 | //// *************************************************************************************** 179 | ////var p = 1.0 180 | ////var q = 0.0 181 | 182 | ////for j in 1...6{ 183 | ////p -= 2 * Double(j) * β[j] * cos(2 * Double(j) * ξ) * cosh(2 * Double(j) * η) 184 | ////q += 2 * Double(j) * β[j] * sin(2 * Double(j) * ξ) * sinh(2 * Double(j) * η) 185 | ////} 186 | 187 | ////let γPrime = atan(tan(ξPrime) * tanh(ηPrime)) 188 | ////let γDoublePrime = atan2(q,p) 189 | 190 | ////let γ = γPrime + γDoublePrime 191 | ////let sinɸ = sin(ɸ) 192 | ////let kPrime = sqrt(1 - e * e * sinɸ * sinɸ) * sqrt(1 + τ * τ) * sqrt(sinhηPrime * sinhηPrime + cosξPrime * cosξPrime) 193 | ////let kDoublePrime = A / a / sqrt(p * p + q * q) 194 | ////let k = k0 + kPrime + kDoublePrime 195 | 196 | let λɸ: Double = ((Double(z) - 1) * 6 - 180 + 3).toRadians() 197 | λ += λɸ //move λ from zonal to global coordinates 198 | 199 | 200 | //round to reasonable precision 201 | let lat = ɸ.toDegrees().toFixed(11) //nm precision (1nm = 10^-11°) 202 | let lon = λ.toDegrees().toFixed(11) 203 | ////let convergence = γ.toDegrees().toFixed(9) 204 | ////let scale = k.toFixed(12) 205 | 206 | return LatLon(lat: lat, lon: lon, datum: self.datum) 207 | } 208 | 209 | public static func parseUTM(utmCoord: String) throws -> Utm{ 210 | let wgsDatum = Datums().datums.filter {$0.name == Datums.wgs84}.first! 211 | return try parseUTM(utmCoord: utmCoord, datum: wgsDatum) 212 | } 213 | 214 | public static func parseUTM(utmCoord: String, datum: Datum) throws -> Utm{ 215 | let foundDatum = DatumFinder.getDatum(desiredDatum: datum) 216 | var utm = utmCoord// "31 N 448251 5411932" 217 | let regex = try! NSRegularExpression(pattern: "\\S+") 218 | utm = utm.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines) 219 | let range = NSMakeRange(0, utm.count) 220 | let matches = regex.matches(in: utm, range: range) 221 | var results = zip(matches, matches.dropFirst().map { Optional.some($0) } + [nil]).map{ current, next -> String in 222 | let newRange = current.range(at: 0) 223 | let start = String.UTF16Index(encodedOffset: newRange.location) 224 | let end = next.map {$0.range(at: 0)}.map { String.UTF16Index(encodedOffset: $0.location)} ?? String.UTF16Index(encodedOffset: utm.utf16.count) 225 | return String(utm.utf16[start.. String{ 241 | return toString(precision: 0) 242 | } 243 | 244 | public func toString(precision: UInt) -> String{ 245 | let z = self.zone < 10 ? "0\(self.zone)" : "\(self.zone)" 246 | let h = self.hemisphere == .n ? "N" : "S" 247 | let e = String(format: "%.0f", self.easting.toFixed(precision)) 248 | let n = String(format: "%.0f", self.northing.toFixed(precision)) 249 | return "\(z) \(h) \(e) \(n)" 250 | } 251 | } 252 | -------------------------------------------------------------------------------- /Coordinates/datums.swift: -------------------------------------------------------------------------------- 1 | // 2 | // datums.swift 3 | // MGRS Converter 4 | // 5 | // Created by John Ayers on 5/1/18. 6 | // Copyright © 2018 Blacksmith Developers. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public struct Datum : Equatable { 12 | public let name: String 13 | public let ellipsoid: Ellipsoid 14 | public let transform: Transform 15 | 16 | public static func ==(lhs: Datum, rhs: Datum) -> Bool { 17 | return (lhs.ellipsoid == rhs.ellipsoid) && (lhs.name == rhs.name) && (lhs.transform == rhs.transform) 18 | } 19 | } 20 | 21 | public struct Transform: Equatable{ 22 | let tx: Double 23 | let ty: Double 24 | let tz: Double 25 | let s: Double 26 | let rx: Double 27 | let ry: Double 28 | let rz: Double 29 | 30 | public static func ==(lhs: Transform, rhs: Transform) -> Bool { 31 | return (lhs.tx == rhs.tx) && (lhs.ty == rhs.ty) && (lhs.tz == rhs.tz) && (lhs.s == rhs.s) && (lhs.tx == rhs.tx) && (lhs.ty == rhs.ty) && (lhs.tz == rhs.tz) 32 | } 33 | } 34 | 35 | public struct Datums{ 36 | let datums: [Datum] 37 | static let wgs84 = "WGS84" 38 | init(){ 39 | let ed50Transform = Transform(tx: 89.5, ty: 93.8, tz: 123.1, s: -1.2, rx: 0.0, ry: 0.0, rz: 0.156) 40 | let irl75Transform = Transform(tx: -482.530, ty: 130.596, tz:-564.557, s:-8.150, rx:-1.042, ry:-0.214, rz:-0.631) 41 | let nad27Transform = Transform(tx: 8, ty: -160, tz: -176, s: 0, rx: 0, ry: 0, rz: 0) 42 | let nad83Transform = Transform(tx: 1.004, ty: -1.910, tz: -0.515, s: -0.0015, rx: 0.0267, ry: 0.00034, rz: 0.011) 43 | let ntfTransform = Transform(tx: 168, ty: 60, tz: -320, s: 0, rx: 0, ry: 0, rz: 0) 44 | let osgbTransform = Transform(tx: -446.448, ty: 125.157, tz: -542.060, s: 20.4894, rx: -0.1502, ry: -0.2470, rz: -0.8421) 45 | let potsdamTransform = Transform(tx: -582, ty: -105, tz: -414, s: -8.3, rx: 1.04, ry: 0.35, rz: -3.08) 46 | let tokyoTransform = Transform(tx: 148, ty: -507, tz: -685, s: 0, rx: 0, ry: 0, rz: 0) 47 | let wgs72Transform = Transform(tx: 0, ty: 0, tz: -4.5, s: -0.22, rx: 0, ry: 0, rz: 0) 48 | let wgs84Transform = Transform(tx: 0, ty: 0, tz: 0, s: 0, rx: 0, ry: 0, rz: 0) 49 | 50 | let ed50 = Datum(name: "ED50", ellipsoid: .Intl1924, transform: ed50Transform) 51 | let irl75 = Datum(name: "Irl1975", ellipsoid: .AiryModified, transform: irl75Transform) 52 | let nad27 = Datum(name: "NAD27", ellipsoid: .Clarke1866, transform: nad27Transform) 53 | let nad83 = Datum(name: "NAD83", ellipsoid: .GRS80, transform: nad83Transform) 54 | let ntf = Datum(name: "NTF", ellipsoid: .Clarke1880IGN, transform: ntfTransform) 55 | let osgb = Datum(name: "OSGB36", ellipsoid: .Airy1830, transform: osgbTransform) 56 | let potsdam = Datum(name: "Potsdam", ellipsoid: .Bessel1841, transform: potsdamTransform) 57 | let tokyo = Datum(name: "TokyoJapan", ellipsoid: .Bessel1841, transform: tokyoTransform) 58 | let wgs72 = Datum(name: "WGS72", ellipsoid: .WGS72, transform: wgs72Transform) 59 | let wgs84 = Datum(name: "WGS84", ellipsoid: .WGS84, transform: wgs84Transform) 60 | 61 | datums = [ed50, irl75, nad27, nad83, ntf, osgb, potsdam, tokyo, wgs72, wgs84] 62 | } 63 | } 64 | 65 | public struct DatumFinder { 66 | public static func getDatum(desiredDatum target: Datum) -> Datum { 67 | let store = Datums().datums 68 | if (store.contains(target)){ 69 | return target 70 | } else{ 71 | return store.filter { $0.name == Datums.wgs84 }.first! 72 | } 73 | } 74 | } 75 | 76 | -------------------------------------------------------------------------------- /Coordinates/dms.swift: -------------------------------------------------------------------------------- 1 | // 2 | // dms.swift 3 | // MGRS Converter 4 | // 5 | // Created by John Ayers on 5/1/18. 6 | // Copyright © 2018 Blacksmith Developers. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public enum DMSFormat{ 12 | case degrees 13 | case degreesMinutes 14 | case degreesMinutesSeconds 15 | } 16 | public enum DMS { 17 | public static func castToDouble(_ data: String) -> Double { 18 | if let data = Double(data){ 19 | return data 20 | } else{ 21 | var mod = data 22 | var i = mod.count 23 | repeat{ 24 | mod = String(mod[.. 0 30 | return 0 31 | } 32 | } 33 | 34 | public static func parseDMS(degMinSec: String) -> Double { 35 | if let parsed = Double(degMinSec) { 36 | return parsed 37 | } 38 | var dms = degMinSec.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines) 39 | var regex = try! NSRegularExpression(pattern: "^-") 40 | // var range = Range(0, in: dms.characters.count) 41 | var range = NSMakeRange(0, dms.count) 42 | dms = regex.stringByReplacingMatches(in: dms, options: [], range: range, withTemplate: "").trimmingCharacters(in: CharacterSet.whitespacesAndNewlines) 43 | regex = try! NSRegularExpression(pattern: "[NSEWnsew]$") 44 | range = NSMakeRange(0, dms.count) 45 | dms = regex.stringByReplacingMatches(in: dms, options: [], range: range, withTemplate: "").trimmingCharacters(in: CharacterSet.whitespacesAndNewlines) 46 | regex = try! NSRegularExpression(pattern: "[0-9.,]+") 47 | range = NSMakeRange(0, dms.count) 48 | let matches = regex.matches(in: dms, range: range) 49 | 50 | var results = zip(matches, matches.dropFirst().map { Optional.some($0) } + [nil]).map{ current, next -> String in 51 | let newRange = current.range(at: 0) 52 | let start = String.UTF16Index(encodedOffset: newRange.location) 53 | let end = next.map {$0.range(at: 0)}.map { String.UTF16Index(encodedOffset: $0.location)} ?? String.UTF16Index(encodedOffset: dms.utf16.count) 54 | return String(dms.utf16[start.. 0{ 80 | deg = deg * -1 81 | } 82 | 83 | return deg 84 | } 85 | 86 | public static func toDMS(deg: Double, format: DMSFormat, decimalPlaces: UInt) -> String{ 87 | let degrees: Double 88 | let dp = Int(decimalPlaces) 89 | let roundingNumber = Double(truncating: pow(10, dp) as NSNumber) 90 | degrees = deg < 0 ? deg * -1 : deg 91 | 92 | var d: Int = 0 93 | var m: Double = 0 94 | var s: Double = 0 95 | var dms: String 96 | 97 | switch format { 98 | case .degrees: 99 | dms = "\(String(format: "%.\(dp)f", degrees))°" 100 | dms = degrees < 100 ? degrees < 10 ? "00\(dms)" : "0\(dms)" : "\(dms)" 101 | case .degreesMinutes: 102 | d = Int(degrees.rounded(.towardZero)) 103 | m = (degrees * 60).truncatingRemainder(dividingBy: 60) 104 | m = (round(roundingNumber * m))/roundingNumber 105 | if (m == 60) { 106 | d += 1 107 | m = 0 108 | } 109 | var minutes = String(format: "%.\(dp)f", m) 110 | minutes = m < 10 ? "0\(minutes)" : minutes 111 | dms = d < 100 ? d < 10 ? "00\(d)" : "0\(d)" : "\(d)" 112 | dms = "\(dms)° \(minutes)'" 113 | case .degreesMinutesSeconds: 114 | d = Int(degrees.rounded(.towardZero)) 115 | m = ((degrees * 3600)/60).truncatingRemainder(dividingBy: 60) 116 | var min = Int(m) 117 | s = (degrees * 3600).truncatingRemainder(dividingBy: 60) 118 | s = (round(roundingNumber * s))/roundingNumber 119 | if (s == 60){ 120 | s = 0 121 | min += 1 122 | } 123 | if (min == 60){ 124 | min = 0 125 | d += 1 126 | } 127 | 128 | let minutes = min < 10 ? "0\(min)" : "\(min)" 129 | var seconds = String(format: "%.\(dp)f", s) 130 | seconds = s < 10 ? "0\(seconds)" : seconds 131 | dms = d < 100 ? d < 10 ? "00\(d)" : "0\(d)" : "\(d)" 132 | dms = "\(dms)° \(minutes)' \(seconds)\"" 133 | } 134 | 135 | return dms 136 | } 137 | 138 | public static func toLat(deg : Double, format: DMSFormat, decimalPlaces: UInt) -> String{ 139 | var lat = toDMS(deg: deg, format: format, decimalPlaces: decimalPlaces) 140 | lat = String(lat[lat.index(lat.startIndex, offsetBy: 1).. String{ 145 | let lon = toDMS(deg: deg, format: format, decimalPlaces: decimalPlaces) 146 | return "\(deg < 0 ? "W" : "E") \(lon)" 147 | } 148 | 149 | public static func toBrng(deg: Double, format: DMSFormat, decimalPlaces: UInt) -> String{ 150 | let degrees = (deg + 360).truncatingRemainder(dividingBy: 360) 151 | var brng = toDMS(deg: degrees, format: format, decimalPlaces: decimalPlaces) 152 | brng = brng.replacingOccurrences(of: "360", with: "0") 153 | return brng 154 | } 155 | 156 | public static func compassPoint(bearing: Double, precision: UInt) -> String{ 157 | let dp = Int(precision) 158 | let brng = (bearing.truncatingRemainder(dividingBy: 360) + 360).truncatingRemainder(dividingBy: 360) 159 | 160 | var cardinals = [ 161 | "N", "NNE", "NE", "ENE", 162 | "E", "ESE", "SE", "SSE", 163 | "S", "SSW", "SW", "WSW", 164 | "W", "WNW", "NW", "NNW" 165 | ] 166 | let n = Double(truncating: 4 * (pow(2, dp-1)) as NSNumber); 167 | let idx = Int(round(brng * n/360).truncatingRemainder(dividingBy: n) * 16/n) 168 | return cardinals[idx] 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /Coordinates/ellipsoids.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ellipsoids.swift 3 | // MGRS Converter 4 | // 5 | // Created by John Ayers on 5/1/18. 6 | // Copyright © 2018 Blacksmith Developers. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public enum Ellipsoid { 12 | case WGS84 13 | case Airy1830 14 | case AiryModified 15 | case Bessel1841 16 | case Clarke1866 17 | case Clarke1880IGN 18 | case GRS80 19 | case Intl1924 20 | case WGS72 21 | 22 | public var a: Double { 23 | switch self { 24 | case .WGS84: 25 | return 6378137 26 | case .Airy1830: 27 | return 6377563.396 28 | case .AiryModified: 29 | return 6377340.189 30 | case .Bessel1841: 31 | return 6377397.155 32 | case .Clarke1866: 33 | return 6378206.4 34 | case .Clarke1880IGN: 35 | return 6378249.2 36 | case .GRS80: 37 | return 6378137 38 | case .Intl1924: 39 | return 6378388 40 | case .WGS72: 41 | return 6378135 42 | } 43 | } 44 | 45 | public var b: Double { 46 | switch self{ 47 | case .WGS84: 48 | return 6356752.314245 49 | case .Airy1830: 50 | return 6356256.909 51 | case .AiryModified: 52 | return 6356034.448 53 | case .Bessel1841: 54 | return 6356078.962818 55 | case .Clarke1866: 56 | return 6356583.8 57 | case .Clarke1880IGN: 58 | return 6356515.0 59 | case .GRS80: 60 | return 6356752.314140 61 | case .Intl1924: 62 | return 6356911.946 63 | case .WGS72: 64 | return 6356750.5 65 | } 66 | } 67 | 68 | public var f: Double { 69 | switch self{ 70 | case .WGS84: 71 | return 1/298.257223563 72 | case .Airy1830: 73 | return 1/299.3249646 74 | case .AiryModified: 75 | return 1/299.3249646 76 | case .Bessel1841: 77 | return 1/299.1528128 78 | case .Clarke1866: 79 | return 1/294.978698214 80 | case .Clarke1880IGN: 81 | return 1/293.466021294 82 | case .GRS80: 83 | return 1/298.257222101 84 | case .Intl1924: 85 | return 1/297 86 | case .WGS72: 87 | return 1/298.26 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /Coordinates/latlon-ellipsoidal.swift: -------------------------------------------------------------------------------- 1 | // 2 | // latlon-ellipsoidal.swift 3 | // MGRS Converter 4 | // 5 | // Created by John Ayers on 5/1/18. 6 | // Copyright © 2018 Blacksmith Developers. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public enum LatLonError: Error{ 12 | case parseError(String) 13 | } 14 | 15 | public struct LatLon : CustomStringConvertible { 16 | 17 | public var description: String { 18 | return self.toString(format: .degreesMinutesSeconds, decimalPlaces: 4) 19 | } 20 | public var lat: Double 21 | public var lon: Double 22 | public var datum: Datum 23 | private let wgs84Datum : Datum 24 | private let datumStore: [Datum] 25 | 26 | public init(lat: Double, lon: Double, datum: Datum) { 27 | datumStore = Datums().datums 28 | self.lat = lat 29 | self.lon = lon 30 | self.wgs84Datum = datumStore.filter { $0.name == Datums.wgs84 }.first! 31 | self.datum = datumStore.filter { $0.name == Datums.wgs84 }.first! 32 | self.datum = getDatum(targetDatum: datum) 33 | } 34 | 35 | public init(lat: Double, lon: Double){ 36 | datumStore = Datums().datums 37 | self.lat = lat 38 | self.lon = lon 39 | self.wgs84Datum = datumStore.filter { $0.name == Datums.wgs84 }.first! 40 | self.datum = datumStore.filter { $0.name == Datums.wgs84 }.first! 41 | self.datum = getDatum(targetDatum: datum) 42 | } 43 | 44 | public func toString(format: DMSFormat, decimalPlaces: UInt, newLinesForEachCoord: Bool) -> String{ 45 | 46 | 47 | return "\(DMS.toLat(deg: self.lat, format: format, decimalPlaces: decimalPlaces)), \(newLinesForEachCoord ? "\n" : "")\(DMS.toLon(deg: self.lon, format: format, decimalPlaces: decimalPlaces))" 48 | } 49 | 50 | public func toString(format: DMSFormat, decimalPlaces: UInt) -> String { 51 | return self.toString(format: format, decimalPlaces: decimalPlaces, newLinesForEachCoord: false) 52 | } 53 | 54 | private func getDatum(targetDatum: Datum) -> Datum{ 55 | if datumStore.contains(targetDatum) { 56 | return targetDatum 57 | } else { 58 | return datumStore.filter { $0.name == Datums.wgs84 }.first! 59 | } 60 | } 61 | 62 | public mutating func convertDatum(toDatum: Datum) -> LatLon{ 63 | let target = getDatum(targetDatum: toDatum) 64 | var transform: Transform? = nil 65 | 66 | if self.datum == wgs84Datum { 67 | transform = target.transform 68 | } 69 | 70 | if (target == wgs84Datum) { 71 | let temp = self.datum.transform 72 | transform = Transform(tx: -1 * temp.tx, ty: -1 * temp.ty, tz: -1 * temp.tz, s: -1 * temp.s, rx: -1 * temp.rx, ry: -1 * temp.ry, rz: -1 * temp.rz) 73 | } 74 | 75 | if (transform == nil){ 76 | //neither this nor the target datum are WGS84; convert to WGS84 first 77 | self = convertDatum(toDatum: wgs84Datum) 78 | transform = target.transform 79 | } 80 | 81 | let oldCartesian = self.toCartesian() 82 | let newCartesian = applyTransform(point: oldCartesian, transform: transform!) 83 | let newLatLon = makeLatLonE(vector: newCartesian, datum: target) 84 | 85 | return newLatLon 86 | } 87 | 88 | public func toCartesian() -> Vector { 89 | let ɸ = self.lat.toRadians() 90 | let λ = self.lon.toRadians() 91 | let h = 0.0 //height above ellipsoid, this is not currently used 92 | let a = self.datum.ellipsoid.a 93 | let f = self.datum.ellipsoid.f 94 | 95 | let sinɸ = sin(ɸ) 96 | let cosɸ = cos(ɸ) 97 | let sinλ = sin(λ) 98 | let cosλ = cos(λ) 99 | 100 | let eSq: Double = 2 * f - f * f //1st eccentricity squared ==> (a^2 - b^2)/a^2 101 | let v = a / sqrt(1 - eSq * sinɸ * sinɸ) //radius of curvature in prime vertical 102 | 103 | let x = (v + h) * cosɸ * cosλ 104 | let y = (v + h) * cosɸ * sinλ 105 | let z = (v * (1 - eSq) + h) * sinɸ 106 | 107 | return Vector(x: x, y: y, z: z) 108 | } 109 | 110 | public func makeLatLonE(vector point:Vector, datum ref: Datum) -> LatLon{ 111 | let x = point.x 112 | let y = point.y 113 | let z = point.z 114 | let a = ref.ellipsoid.a 115 | let b = ref.ellipsoid.b 116 | let f = ref.ellipsoid.f 117 | let e2 = 2 * f - f * f //1st eccentricity squared ==> (a^2 - b^2)/a^2 118 | let ε2 = e2 / (1 - e2) //2nd eccentricity squared ==> (a^2 - b^2)/a^2 119 | let p = sqrt(x * x + y * y) //distance from minor axis 120 | let r = sqrt(p * p + z * z) //polar radius 121 | 122 | //parametric latitude 123 | let tanβ = (b * z) / (a * p) * (1 + ε2 * b / r) 124 | let sinβ = tanβ / sqrt(1 + tanβ * tanβ) 125 | let cosβ = sinβ / tanβ 126 | 127 | //geodetic latitude 128 | let ɸ = cosβ.isNaN ? 0 : sqrt(atan2(z + ε2 * b * sinβ * sinβ * sinβ, p - e2 * a * cosβ * cosβ * cosβ)) 129 | let λ = atan2(y, x) 130 | 131 | //height above ellipsoid, not currently used 132 | // let sinɸ = sin(ɸ) 133 | // let cosɸ = cos(ɸ) 134 | // let v = a / sqrt(1 - e2 * sinɸ * sinɸ) //length of the normal terminated by the minor axis 135 | // let h = p * cosɸ + z * sinɸ - (a * a / v) 136 | 137 | return LatLon(lat: ɸ.toDegrees(), lon: λ.toDegrees(), datum: ref) 138 | } 139 | 140 | public func applyTransform(point: Vector, transform: Transform) -> Vector { 141 | let x1 = point.x 142 | let y1 = point.y 143 | let z1 = point.z 144 | let sFloat: Double = 1e6 145 | let tx = transform.tx 146 | let ty = transform.ty 147 | let tz = transform.tz 148 | let s1 = transform.s / sFloat + 1 149 | let rx = (transform.rx / 3600).toRadians() 150 | let ry = (transform.ry / 3600).toRadians() 151 | let rz = (transform.rz / 3600).toRadians() 152 | 153 | let x2 = tx + x1 * s1 - y1 * rz + z1 * ry 154 | let y2 = ty + x1 * rz + y1 * s1 - z1 * rx 155 | let z2 = tz + x1 * ry + y1 * rx + z1 * s1 156 | 157 | return Vector(x: x2, y: y2, z: z2) 158 | } 159 | 160 | public static func parseLatLon(stringToParse val: String) throws -> LatLon { 161 | guard var idx = val.index(of: ",") else{ 162 | throw LatLonError.parseError("Invalid string") 163 | } 164 | let strLat = val[val.startIndex.. 0 && strLon.count > 0 else { 169 | throw LatLonError.parseError("Invalid string") 170 | } 171 | var matchFound: Bool = false 172 | var regex = try! NSRegularExpression(pattern: "\\A[Nn]") 173 | var range = NSMakeRange(0, strLat.count) 174 | var changes = regex.stringByReplacingMatches(in: strLat, options: [], range: range, withTemplate: "") 175 | if strLat.count != changes.count{ 176 | changes = "\(changes.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)) N" 177 | matchFound = true 178 | } 179 | if !matchFound { 180 | regex = try! NSRegularExpression(pattern: "[Nn]$") 181 | changes = regex.stringByReplacingMatches(in: strLat, options: [], range: range, withTemplate: "") 182 | if strLat.count != changes.count{ 183 | changes = "\(changes.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)) N" 184 | matchFound = true 185 | } 186 | } 187 | 188 | if !matchFound { 189 | regex = try! NSRegularExpression(pattern: "\\A[Ss]") 190 | changes = regex.stringByReplacingMatches(in: strLat, options: [], range: range, withTemplate: "") 191 | if (strLat.count != changes.count){ 192 | changes = "\(changes.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)) S" 193 | matchFound = true 194 | } 195 | } 196 | 197 | if !matchFound { 198 | regex = try! NSRegularExpression(pattern: "[Ss]$") 199 | changes = regex.stringByReplacingMatches(in: strLat, options: [], range: range, withTemplate: "") 200 | if (strLat.count != changes.count){ 201 | changes = "\(changes.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)) S" 202 | matchFound = true 203 | } 204 | } 205 | 206 | if !matchFound { 207 | regex = try! NSRegularExpression(pattern: "\\A([-])") 208 | changes = regex.stringByReplacingMatches(in: strLat, options: [], range: range, withTemplate: "") 209 | if (strLat.count != changes.count){ 210 | changes = "\(changes.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)) S" 211 | matchFound = true 212 | } 213 | else { 214 | changes = "\(changes.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)) N" 215 | matchFound = true 216 | } 217 | } 218 | 219 | range = NSMakeRange(0, changes.count) 220 | regex = try! NSRegularExpression(pattern: "[abcdefghijklmopqrtuvwxyzABCDEFGHIJKLMOPQRTUVWXYZ]+") 221 | changes = regex.stringByReplacingMatches(in: changes, options: [], range: range, withTemplate: "") 222 | 223 | guard matchFound else { 224 | throw LatLonError.parseError("Invalid string") 225 | } 226 | let convertableLat = changes //we've got one, so we can store it 227 | matchFound = false 228 | 229 | regex = try! NSRegularExpression(pattern: "\\A[Ee]") 230 | range = NSMakeRange(0, strLon.count) 231 | changes = regex.stringByReplacingMatches(in: strLon, options: [], range: range, withTemplate: "") 232 | if strLon.count != changes.count{ 233 | changes = "\(changes.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)) E" 234 | matchFound = true 235 | } 236 | if !matchFound { 237 | regex = try! NSRegularExpression(pattern: "[Ee]$") 238 | changes = regex.stringByReplacingMatches(in: strLon, options: [], range: range, withTemplate: "") 239 | if strLon.count != changes.count{ 240 | changes = "\(changes.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)) E" 241 | matchFound = true 242 | } 243 | } 244 | 245 | if !matchFound { 246 | regex = try! NSRegularExpression(pattern: "\\A[Ww]") 247 | changes = regex.stringByReplacingMatches(in: strLon, options: [], range: range, withTemplate: "") 248 | if (strLon.count != changes.count){ 249 | changes = "\(changes.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)) W" 250 | matchFound = true 251 | } 252 | } 253 | 254 | if !matchFound { 255 | regex = try! NSRegularExpression(pattern: "[Ww]$") 256 | changes = regex.stringByReplacingMatches(in: strLon, options: [], range: range, withTemplate: "") 257 | if (strLon.count != changes.count){ 258 | changes = "\(changes.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)) W" 259 | matchFound = true 260 | } 261 | } 262 | 263 | if !matchFound { 264 | regex = try! NSRegularExpression(pattern: "\\A([-])") 265 | changes = regex.stringByReplacingMatches(in: strLon, options: [], range: range, withTemplate: "") 266 | if (strLon.count != changes.count){ 267 | changes = "\(changes.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)) W" 268 | matchFound = true 269 | } 270 | else { 271 | changes = "\(changes.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)) E" 272 | matchFound = true 273 | } 274 | } 275 | 276 | range = NSMakeRange(0, changes.count) 277 | regex = try! NSRegularExpression(pattern: "[abcdfghijklmnopqrstuvxyzABCDFGHIJKLMNOPQRSTUVXYZ]+") 278 | changes = regex.stringByReplacingMatches(in: changes, options: [], range: range, withTemplate: "") 279 | 280 | guard matchFound else { 281 | throw LatLonError.parseError("Invalid string") 282 | } 283 | 284 | let convertableLon = changes 285 | 286 | let lat = DMS.parseDMS(degMinSec: convertableLat) 287 | let lon = DMS.parseDMS(degMinSec: convertableLon) 288 | let wgsDatum = Datums().datums.filter {$0.name == Datums.wgs84}.first! 289 | 290 | return LatLon(lat: lat, lon: lon, datum: wgsDatum) 291 | } 292 | } 293 | -------------------------------------------------------------------------------- /Coordinates/vector.swift: -------------------------------------------------------------------------------- 1 | // 2 | // vector.swift 3 | // MGRS Converter 4 | // 5 | // Created by John Ayers on 5/1/18. 6 | // Copyright © 2018 Blacksmith Developers. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public struct Vector: Equatable, CustomStringConvertible { 12 | 13 | public var description:String { 14 | let xString = String(format: "%.5f", self.x) 15 | let yString = String(format: "%.5f", self.y) 16 | let zString = String(format: "%.5f", self.z) 17 | return "[\(xString), \(yString), \(zString)]" 18 | } 19 | 20 | public var x: Double 21 | public var y: Double 22 | public var z: Double 23 | 24 | public init(x: Double, y: Double, z: Double) { 25 | self.x = x 26 | self.y = y 27 | self.z = z 28 | } 29 | 30 | public static func ==(lhs: Vector, rhs: Vector) -> Bool { 31 | return (lhs.x == rhs.x) && (lhs.y == rhs.y) && (lhs.z == rhs.z) 32 | } 33 | 34 | public static func +(lhs: Vector, rhs: Vector) -> Vector{ 35 | return Vector(x: lhs.x + rhs.x, y: lhs.y + rhs.y, z: lhs.z + rhs.z) 36 | } 37 | 38 | public static func -(lhs: Vector, rhs: Vector) -> Vector{ 39 | return Vector(x: lhs.x - rhs.x, y: lhs.y - rhs.y, z: lhs.z - rhs.z) 40 | } 41 | 42 | public static func *(lhs: Vector, rhs: Vector) -> Double{ 43 | return (lhs.x * rhs.x) + (lhs.y * rhs.y) + (lhs.z * rhs.z) 44 | } 45 | 46 | public static func /(lhs: Vector, rhs: Vector) -> Double{ 47 | return (lhs.x / rhs.x) + (lhs.y / rhs.y) + (lhs.z / rhs.z) 48 | } 49 | 50 | public func times(x: Double) -> Vector{ 51 | return Vector(x: self.x * x, y: self.y * x, z: self.z * x) 52 | } 53 | 54 | public func divideBy(x: Double) -> Vector{ 55 | return Vector(x: self.x / x, y: self.y / y, z: self.z / z) 56 | } 57 | 58 | public func dot(secondVector: Vector) -> Double{ 59 | return self * secondVector 60 | } 61 | 62 | public func cross(secondVector: Vector) -> Vector { 63 | let x = (self.y * secondVector.z) - (self.z * secondVector.y) 64 | let y = (self.z * secondVector.x) - (self.x * secondVector.z) 65 | let z = (self.x * secondVector.y) - (self.y * secondVector.x) 66 | return Vector(x: x, y: y, z: z) 67 | } 68 | 69 | public func negate() -> Vector { 70 | return Vector(x: self.x * -1, y: self.y * -1, z: self.z * -1) 71 | } 72 | 73 | public func length() -> Double{ 74 | return sqrt((self.x * self.x) + (self.y * self.y) + (self.z + self.z)) 75 | } 76 | 77 | public func unit() -> Vector{ 78 | let norm = self.length() 79 | if (norm == 1) { return self } 80 | if (norm == 0) { return self } 81 | 82 | let x = self.x / norm 83 | let y = self.y / norm 84 | let z = self.z / norm 85 | return Vector(x: x, y: y, z: z) 86 | } 87 | 88 | //returns angle in radians 89 | public func angleTo(vector1: Vector, planeNormal: Vector = Vector(x: 999, y: 999, z: 999)) -> Double{ 90 | 91 | let mathSign: Double 92 | if (planeNormal == Vector(x: 999, y: 999, z: 999)){ 93 | mathSign = 1.0 94 | } else { 95 | mathSign = Double.sign(self.cross(secondVector: vector1).dot(secondVector: planeNormal)) 96 | } 97 | 98 | let sinTheta = self.cross(secondVector: vector1).length() * mathSign 99 | let cosTheta = self.dot(secondVector: vector1) 100 | 101 | return atan2(sinTheta, cosTheta) 102 | } 103 | 104 | public func rotateAround(axis: Vector, theta: Double) -> Vector{ 105 | let p1 = self.unit() 106 | let p = [p1.x, p1.y, p1.z] //the point being rotated 107 | let a = axis.unit() //the axis being rotated around 108 | let s = sin(theta) 109 | let c = cos(theta) 110 | let q1 = Vector(x: a.x * a.x * (1 - c) + c, y: a.x * a.y * (1 - c) - a.z * s, z: a.x * a.z * (1 - c) + a.y * s) 111 | let q2 = Vector(x: a.y * a.x * (1 - c) + a.z * s, y: a.y * a.y * (1 - c) + c, z: a.y * a.z * (1 - c) - a.x * s) 112 | let q3 = Vector(x: a.z * a.x * (1 - c) - a.y * s, y: a.z * a.y * (1 - c) + a.x * s, z: a.z * a.z * (1 - c) + c) 113 | /*let q = [ 114 | [ a.x * a.x * (1 - c) + c, a.x * a.y * (1 - c) - a.z * s, a.x * a.z * (1 - c) + a.y * s ], 115 | [ a.y * a.x * (1 - c) + a.z * s, a.y * a.y * (1 - c) + c, a.y * a.z * (1 - c) - a.x * s ], 116 | [ a.z * a.x * (1 - c) - a.y * s, a.z * a.y * (1 - c) + a.x * s, a.z * a.z * (1 - c) + c ] 117 | ]*/ 118 | let q = [ 119 | [ q1.x, q1.y, q1.z], 120 | [ q2.x, q2.y, q2.z], 121 | [ q3.x, q3.y, q3.z] 122 | ] 123 | 124 | var qp: [Double] = [0, 0, 0] 125 | for i in 0...2 { 126 | for j in 0...2{ 127 | qp[i] += q[i][j] * p[j] 128 | } 129 | } 130 | return Vector(x: qp[0], y: qp[1], z: qp[3]) 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 blacksmithdevelopers 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MGRS Converter.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | F925FC302098A50E000134F0 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F925FC2F2098A50E000134F0 /* AppDelegate.swift */; }; 11 | F925FC322098A50E000134F0 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F925FC312098A50E000134F0 /* ViewController.swift */; }; 12 | F925FC352098A50E000134F0 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F925FC332098A50E000134F0 /* Main.storyboard */; }; 13 | F925FC3A2098A50F000134F0 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F925FC382098A50F000134F0 /* LaunchScreen.storyboard */; }; 14 | F932AF18209D435600BDB9E5 /* MapViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F932AF17209D435600BDB9E5 /* MapViewController.swift */; }; 15 | F940C462209D40AA00352060 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F940C461209D40AA00352060 /* Assets.xcassets */; }; 16 | F9422578209B5FD5005D4287 /* globe.PNG in Resources */ = {isa = PBXBuildFile; fileRef = F9422577209B5FD5005D4287 /* globe.PNG */; }; 17 | F981FE9E2113BE3E0089A553 /* Coordinates.h in Headers */ = {isa = PBXBuildFile; fileRef = F981FE9C2113BE3E0089A553 /* Coordinates.h */; settings = {ATTRIBUTES = (Public, ); }; }; 18 | F981FEA12113BE3E0089A553 /* Coordinates.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F981FE9A2113BE3E0089A553 /* Coordinates.framework */; }; 19 | F981FEA22113BE3E0089A553 /* Coordinates.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = F981FE9A2113BE3E0089A553 /* Coordinates.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 20 | F981FEA72113BE510089A553 /* String+toCharacter.swift in Sources */ = {isa = PBXBuildFile; fileRef = F949A4F4209A2F5400B3F633 /* String+toCharacter.swift */; }; 21 | F981FEA82113BE510089A553 /* UTM+toMGRS.swift in Sources */ = {isa = PBXBuildFile; fileRef = F949A4F2209A28A000B3F633 /* UTM+toMGRS.swift */; }; 22 | F981FEA92113BE510089A553 /* dms.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9E59D992098D72D00CDB60A /* dms.swift */; }; 23 | F981FEAA2113BE510089A553 /* MGRS.swift in Sources */ = {isa = PBXBuildFile; fileRef = F949A4F0209A14F600B3F633 /* MGRS.swift */; }; 24 | F981FEAB2113BE510089A553 /* UTM.swift in Sources */ = {isa = PBXBuildFile; fileRef = F949A4EC209953FE00B3F633 /* UTM.swift */; }; 25 | F981FEAC2113BE510089A553 /* Double+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F925FC442098B377000134F0 /* Double+Extensions.swift */; }; 26 | F981FEAD2113BE510089A553 /* latlon-ellipsoidal.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9FECE162099135000805089 /* latlon-ellipsoidal.swift */; }; 27 | F981FEAE2113BE510089A553 /* datums.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9FECE1A20991FBF00805089 /* datums.swift */; }; 28 | F981FEAF2113BE510089A553 /* vector.swift in Sources */ = {isa = PBXBuildFile; fileRef = F925FC422098A580000134F0 /* vector.swift */; }; 29 | F981FEB02113BE510089A553 /* ellipsoids.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9FECE182099186B00805089 /* ellipsoids.swift */; }; 30 | F981FEB12113BE510089A553 /* String+getIntegerPosition.swift in Sources */ = {isa = PBXBuildFile; fileRef = F949A4F6209A3BD900B3F633 /* String+getIntegerPosition.swift */; }; 31 | F981FEB22113BE510089A553 /* LatLon+toUTM.swift in Sources */ = {isa = PBXBuildFile; fileRef = F949A4EE20995BAE00B3F633 /* LatLon+toUTM.swift */; }; 32 | F98F81D9209E4A480094B4C0 /* MapKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F98F81D8209E4A480094B4C0 /* MapKit.framework */; }; 33 | /* End PBXBuildFile section */ 34 | 35 | /* Begin PBXContainerItemProxy section */ 36 | F981FE9F2113BE3E0089A553 /* PBXContainerItemProxy */ = { 37 | isa = PBXContainerItemProxy; 38 | containerPortal = F925FC242098A50E000134F0 /* Project object */; 39 | proxyType = 1; 40 | remoteGlobalIDString = F981FE992113BE3E0089A553; 41 | remoteInfo = Coordinates; 42 | }; 43 | /* End PBXContainerItemProxy section */ 44 | 45 | /* Begin PBXCopyFilesBuildPhase section */ 46 | F981FEA62113BE3E0089A553 /* Embed Frameworks */ = { 47 | isa = PBXCopyFilesBuildPhase; 48 | buildActionMask = 2147483647; 49 | dstPath = ""; 50 | dstSubfolderSpec = 10; 51 | files = ( 52 | F981FEA22113BE3E0089A553 /* Coordinates.framework in Embed Frameworks */, 53 | ); 54 | name = "Embed Frameworks"; 55 | runOnlyForDeploymentPostprocessing = 0; 56 | }; 57 | /* End PBXCopyFilesBuildPhase section */ 58 | 59 | /* Begin PBXFileReference section */ 60 | F925FC2C2098A50E000134F0 /* MGRS Converter.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "MGRS Converter.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 61 | F925FC2F2098A50E000134F0 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 62 | F925FC312098A50E000134F0 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 63 | F925FC342098A50E000134F0 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 64 | F925FC392098A50F000134F0 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 65 | F925FC3B2098A50F000134F0 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 66 | F925FC422098A580000134F0 /* vector.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = vector.swift; sourceTree = ""; }; 67 | F925FC442098B377000134F0 /* Double+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Double+Extensions.swift"; sourceTree = ""; }; 68 | F932AF17209D435600BDB9E5 /* MapViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapViewController.swift; sourceTree = ""; }; 69 | F940C461209D40AA00352060 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 70 | F9422577209B5FD5005D4287 /* globe.PNG */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = globe.PNG; sourceTree = ""; }; 71 | F949A4EC209953FE00B3F633 /* UTM.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UTM.swift; sourceTree = ""; }; 72 | F949A4EE20995BAE00B3F633 /* LatLon+toUTM.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "LatLon+toUTM.swift"; sourceTree = ""; }; 73 | F949A4F0209A14F600B3F633 /* MGRS.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MGRS.swift; sourceTree = ""; }; 74 | F949A4F2209A28A000B3F633 /* UTM+toMGRS.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UTM+toMGRS.swift"; sourceTree = ""; }; 75 | F949A4F4209A2F5400B3F633 /* String+toCharacter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+toCharacter.swift"; sourceTree = ""; }; 76 | F949A4F6209A3BD900B3F633 /* String+getIntegerPosition.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+getIntegerPosition.swift"; sourceTree = ""; }; 77 | F981FE9A2113BE3E0089A553 /* Coordinates.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Coordinates.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 78 | F981FE9C2113BE3E0089A553 /* Coordinates.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Coordinates.h; sourceTree = ""; }; 79 | F981FE9D2113BE3E0089A553 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 80 | F98F81D8209E4A480094B4C0 /* MapKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MapKit.framework; path = System/Library/Frameworks/MapKit.framework; sourceTree = SDKROOT; }; 81 | F9E59D992098D72D00CDB60A /* dms.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = dms.swift; sourceTree = ""; }; 82 | F9FECE162099135000805089 /* latlon-ellipsoidal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "latlon-ellipsoidal.swift"; sourceTree = ""; }; 83 | F9FECE182099186B00805089 /* ellipsoids.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ellipsoids.swift; sourceTree = ""; }; 84 | F9FECE1A20991FBF00805089 /* datums.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = datums.swift; sourceTree = ""; }; 85 | /* End PBXFileReference section */ 86 | 87 | /* Begin PBXFrameworksBuildPhase section */ 88 | F925FC292098A50E000134F0 /* Frameworks */ = { 89 | isa = PBXFrameworksBuildPhase; 90 | buildActionMask = 2147483647; 91 | files = ( 92 | F981FEA12113BE3E0089A553 /* Coordinates.framework in Frameworks */, 93 | F98F81D9209E4A480094B4C0 /* MapKit.framework in Frameworks */, 94 | ); 95 | runOnlyForDeploymentPostprocessing = 0; 96 | }; 97 | F981FE962113BE3E0089A553 /* Frameworks */ = { 98 | isa = PBXFrameworksBuildPhase; 99 | buildActionMask = 2147483647; 100 | files = ( 101 | ); 102 | runOnlyForDeploymentPostprocessing = 0; 103 | }; 104 | /* End PBXFrameworksBuildPhase section */ 105 | 106 | /* Begin PBXGroup section */ 107 | F925FC232098A50E000134F0 = { 108 | isa = PBXGroup; 109 | children = ( 110 | F925FC2E2098A50E000134F0 /* MGRS Converter */, 111 | F981FE9B2113BE3E0089A553 /* Coordinates */, 112 | F925FC2D2098A50E000134F0 /* Products */, 113 | F98F81D7209E4A480094B4C0 /* Frameworks */, 114 | ); 115 | sourceTree = ""; 116 | }; 117 | F925FC2D2098A50E000134F0 /* Products */ = { 118 | isa = PBXGroup; 119 | children = ( 120 | F925FC2C2098A50E000134F0 /* MGRS Converter.app */, 121 | F981FE9A2113BE3E0089A553 /* Coordinates.framework */, 122 | ); 123 | name = Products; 124 | sourceTree = ""; 125 | }; 126 | F925FC2E2098A50E000134F0 /* MGRS Converter */ = { 127 | isa = PBXGroup; 128 | children = ( 129 | F925FC412098A533000134F0 /* Models */, 130 | F925FC2F2098A50E000134F0 /* AppDelegate.swift */, 131 | F925FC312098A50E000134F0 /* ViewController.swift */, 132 | F940C461209D40AA00352060 /* Assets.xcassets */, 133 | F925FC332098A50E000134F0 /* Main.storyboard */, 134 | F932AF17209D435600BDB9E5 /* MapViewController.swift */, 135 | F925FC382098A50F000134F0 /* LaunchScreen.storyboard */, 136 | F925FC3B2098A50F000134F0 /* Info.plist */, 137 | F9422577209B5FD5005D4287 /* globe.PNG */, 138 | ); 139 | path = "MGRS Converter"; 140 | sourceTree = ""; 141 | }; 142 | F925FC412098A533000134F0 /* Models */ = { 143 | isa = PBXGroup; 144 | children = ( 145 | ); 146 | path = Models; 147 | sourceTree = ""; 148 | }; 149 | F981FE9B2113BE3E0089A553 /* Coordinates */ = { 150 | isa = PBXGroup; 151 | children = ( 152 | F925FC422098A580000134F0 /* vector.swift */, 153 | F9FECE162099135000805089 /* latlon-ellipsoidal.swift */, 154 | F9FECE182099186B00805089 /* ellipsoids.swift */, 155 | F9FECE1A20991FBF00805089 /* datums.swift */, 156 | F9E59D992098D72D00CDB60A /* dms.swift */, 157 | F925FC442098B377000134F0 /* Double+Extensions.swift */, 158 | F949A4EC209953FE00B3F633 /* UTM.swift */, 159 | F949A4F0209A14F600B3F633 /* MGRS.swift */, 160 | F949A4F6209A3BD900B3F633 /* String+getIntegerPosition.swift */, 161 | F949A4F2209A28A000B3F633 /* UTM+toMGRS.swift */, 162 | F949A4F4209A2F5400B3F633 /* String+toCharacter.swift */, 163 | F949A4EE20995BAE00B3F633 /* LatLon+toUTM.swift */, 164 | F981FE9C2113BE3E0089A553 /* Coordinates.h */, 165 | F981FE9D2113BE3E0089A553 /* Info.plist */, 166 | ); 167 | path = Coordinates; 168 | sourceTree = ""; 169 | }; 170 | F98F81D7209E4A480094B4C0 /* Frameworks */ = { 171 | isa = PBXGroup; 172 | children = ( 173 | F98F81D8209E4A480094B4C0 /* MapKit.framework */, 174 | ); 175 | name = Frameworks; 176 | sourceTree = ""; 177 | }; 178 | /* End PBXGroup section */ 179 | 180 | /* Begin PBXHeadersBuildPhase section */ 181 | F981FE972113BE3E0089A553 /* Headers */ = { 182 | isa = PBXHeadersBuildPhase; 183 | buildActionMask = 2147483647; 184 | files = ( 185 | F981FE9E2113BE3E0089A553 /* Coordinates.h in Headers */, 186 | ); 187 | runOnlyForDeploymentPostprocessing = 0; 188 | }; 189 | /* End PBXHeadersBuildPhase section */ 190 | 191 | /* Begin PBXNativeTarget section */ 192 | F925FC2B2098A50E000134F0 /* MGRS Converter */ = { 193 | isa = PBXNativeTarget; 194 | buildConfigurationList = F925FC3E2098A50F000134F0 /* Build configuration list for PBXNativeTarget "MGRS Converter" */; 195 | buildPhases = ( 196 | F925FC282098A50E000134F0 /* Sources */, 197 | F925FC292098A50E000134F0 /* Frameworks */, 198 | F925FC2A2098A50E000134F0 /* Resources */, 199 | F981FEA62113BE3E0089A553 /* Embed Frameworks */, 200 | ); 201 | buildRules = ( 202 | ); 203 | dependencies = ( 204 | F981FEA02113BE3E0089A553 /* PBXTargetDependency */, 205 | ); 206 | name = "MGRS Converter"; 207 | productName = "MGRS Converter"; 208 | productReference = F925FC2C2098A50E000134F0 /* MGRS Converter.app */; 209 | productType = "com.apple.product-type.application"; 210 | }; 211 | F981FE992113BE3E0089A553 /* Coordinates */ = { 212 | isa = PBXNativeTarget; 213 | buildConfigurationList = F981FEA52113BE3E0089A553 /* Build configuration list for PBXNativeTarget "Coordinates" */; 214 | buildPhases = ( 215 | F981FE952113BE3E0089A553 /* Sources */, 216 | F981FE962113BE3E0089A553 /* Frameworks */, 217 | F981FE972113BE3E0089A553 /* Headers */, 218 | F981FE982113BE3E0089A553 /* Resources */, 219 | ); 220 | buildRules = ( 221 | ); 222 | dependencies = ( 223 | ); 224 | name = Coordinates; 225 | productName = Coordinates; 226 | productReference = F981FE9A2113BE3E0089A553 /* Coordinates.framework */; 227 | productType = "com.apple.product-type.framework"; 228 | }; 229 | /* End PBXNativeTarget section */ 230 | 231 | /* Begin PBXProject section */ 232 | F925FC242098A50E000134F0 /* Project object */ = { 233 | isa = PBXProject; 234 | attributes = { 235 | LastSwiftUpdateCheck = 0930; 236 | LastUpgradeCheck = 0930; 237 | ORGANIZATIONNAME = "Blacksmith Developers"; 238 | TargetAttributes = { 239 | F925FC2B2098A50E000134F0 = { 240 | CreatedOnToolsVersion = 9.3; 241 | SystemCapabilities = { 242 | com.apple.Maps.iOS = { 243 | enabled = 1; 244 | }; 245 | }; 246 | }; 247 | F981FE992113BE3E0089A553 = { 248 | CreatedOnToolsVersion = 9.4.1; 249 | }; 250 | }; 251 | }; 252 | buildConfigurationList = F925FC272098A50E000134F0 /* Build configuration list for PBXProject "MGRS Converter" */; 253 | compatibilityVersion = "Xcode 9.3"; 254 | developmentRegion = en; 255 | hasScannedForEncodings = 0; 256 | knownRegions = ( 257 | en, 258 | Base, 259 | ); 260 | mainGroup = F925FC232098A50E000134F0; 261 | productRefGroup = F925FC2D2098A50E000134F0 /* Products */; 262 | projectDirPath = ""; 263 | projectRoot = ""; 264 | targets = ( 265 | F925FC2B2098A50E000134F0 /* MGRS Converter */, 266 | F981FE992113BE3E0089A553 /* Coordinates */, 267 | ); 268 | }; 269 | /* End PBXProject section */ 270 | 271 | /* Begin PBXResourcesBuildPhase section */ 272 | F925FC2A2098A50E000134F0 /* Resources */ = { 273 | isa = PBXResourcesBuildPhase; 274 | buildActionMask = 2147483647; 275 | files = ( 276 | F925FC3A2098A50F000134F0 /* LaunchScreen.storyboard in Resources */, 277 | F9422578209B5FD5005D4287 /* globe.PNG in Resources */, 278 | F940C462209D40AA00352060 /* Assets.xcassets in Resources */, 279 | F925FC352098A50E000134F0 /* Main.storyboard in Resources */, 280 | ); 281 | runOnlyForDeploymentPostprocessing = 0; 282 | }; 283 | F981FE982113BE3E0089A553 /* Resources */ = { 284 | isa = PBXResourcesBuildPhase; 285 | buildActionMask = 2147483647; 286 | files = ( 287 | ); 288 | runOnlyForDeploymentPostprocessing = 0; 289 | }; 290 | /* End PBXResourcesBuildPhase section */ 291 | 292 | /* Begin PBXSourcesBuildPhase section */ 293 | F925FC282098A50E000134F0 /* Sources */ = { 294 | isa = PBXSourcesBuildPhase; 295 | buildActionMask = 2147483647; 296 | files = ( 297 | F925FC322098A50E000134F0 /* ViewController.swift in Sources */, 298 | F925FC302098A50E000134F0 /* AppDelegate.swift in Sources */, 299 | F932AF18209D435600BDB9E5 /* MapViewController.swift in Sources */, 300 | ); 301 | runOnlyForDeploymentPostprocessing = 0; 302 | }; 303 | F981FE952113BE3E0089A553 /* Sources */ = { 304 | isa = PBXSourcesBuildPhase; 305 | buildActionMask = 2147483647; 306 | files = ( 307 | F981FEAC2113BE510089A553 /* Double+Extensions.swift in Sources */, 308 | F981FEAB2113BE510089A553 /* UTM.swift in Sources */, 309 | F981FEAF2113BE510089A553 /* vector.swift in Sources */, 310 | F981FEB22113BE510089A553 /* LatLon+toUTM.swift in Sources */, 311 | F981FEB12113BE510089A553 /* String+getIntegerPosition.swift in Sources */, 312 | F981FEA82113BE510089A553 /* UTM+toMGRS.swift in Sources */, 313 | F981FEA92113BE510089A553 /* dms.swift in Sources */, 314 | F981FEAA2113BE510089A553 /* MGRS.swift in Sources */, 315 | F981FEAE2113BE510089A553 /* datums.swift in Sources */, 316 | F981FEA72113BE510089A553 /* String+toCharacter.swift in Sources */, 317 | F981FEAD2113BE510089A553 /* latlon-ellipsoidal.swift in Sources */, 318 | F981FEB02113BE510089A553 /* ellipsoids.swift in Sources */, 319 | ); 320 | runOnlyForDeploymentPostprocessing = 0; 321 | }; 322 | /* End PBXSourcesBuildPhase section */ 323 | 324 | /* Begin PBXTargetDependency section */ 325 | F981FEA02113BE3E0089A553 /* PBXTargetDependency */ = { 326 | isa = PBXTargetDependency; 327 | target = F981FE992113BE3E0089A553 /* Coordinates */; 328 | targetProxy = F981FE9F2113BE3E0089A553 /* PBXContainerItemProxy */; 329 | }; 330 | /* End PBXTargetDependency section */ 331 | 332 | /* Begin PBXVariantGroup section */ 333 | F925FC332098A50E000134F0 /* Main.storyboard */ = { 334 | isa = PBXVariantGroup; 335 | children = ( 336 | F925FC342098A50E000134F0 /* Base */, 337 | ); 338 | name = Main.storyboard; 339 | sourceTree = ""; 340 | }; 341 | F925FC382098A50F000134F0 /* LaunchScreen.storyboard */ = { 342 | isa = PBXVariantGroup; 343 | children = ( 344 | F925FC392098A50F000134F0 /* Base */, 345 | ); 346 | name = LaunchScreen.storyboard; 347 | sourceTree = ""; 348 | }; 349 | /* End PBXVariantGroup section */ 350 | 351 | /* Begin XCBuildConfiguration section */ 352 | F925FC3C2098A50F000134F0 /* Debug */ = { 353 | isa = XCBuildConfiguration; 354 | buildSettings = { 355 | ALWAYS_SEARCH_USER_PATHS = NO; 356 | CLANG_ANALYZER_NONNULL = YES; 357 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 358 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 359 | CLANG_CXX_LIBRARY = "libc++"; 360 | CLANG_ENABLE_MODULES = YES; 361 | CLANG_ENABLE_OBJC_ARC = YES; 362 | CLANG_ENABLE_OBJC_WEAK = YES; 363 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 364 | CLANG_WARN_BOOL_CONVERSION = YES; 365 | CLANG_WARN_COMMA = YES; 366 | CLANG_WARN_CONSTANT_CONVERSION = YES; 367 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 368 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 369 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 370 | CLANG_WARN_EMPTY_BODY = YES; 371 | CLANG_WARN_ENUM_CONVERSION = YES; 372 | CLANG_WARN_INFINITE_RECURSION = YES; 373 | CLANG_WARN_INT_CONVERSION = YES; 374 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 375 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 376 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 377 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 378 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 379 | CLANG_WARN_STRICT_PROTOTYPES = YES; 380 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 381 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 382 | CLANG_WARN_UNREACHABLE_CODE = YES; 383 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 384 | CODE_SIGN_IDENTITY = "iPhone Developer"; 385 | COPY_PHASE_STRIP = NO; 386 | DEBUG_INFORMATION_FORMAT = dwarf; 387 | ENABLE_STRICT_OBJC_MSGSEND = YES; 388 | ENABLE_TESTABILITY = YES; 389 | GCC_C_LANGUAGE_STANDARD = gnu11; 390 | GCC_DYNAMIC_NO_PIC = NO; 391 | GCC_NO_COMMON_BLOCKS = YES; 392 | GCC_OPTIMIZATION_LEVEL = 0; 393 | GCC_PREPROCESSOR_DEFINITIONS = ( 394 | "DEBUG=1", 395 | "$(inherited)", 396 | ); 397 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 398 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 399 | GCC_WARN_UNDECLARED_SELECTOR = YES; 400 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 401 | GCC_WARN_UNUSED_FUNCTION = YES; 402 | GCC_WARN_UNUSED_VARIABLE = YES; 403 | IPHONEOS_DEPLOYMENT_TARGET = 11.3; 404 | MTL_ENABLE_DEBUG_INFO = YES; 405 | ONLY_ACTIVE_ARCH = YES; 406 | SDKROOT = iphoneos; 407 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 408 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 409 | }; 410 | name = Debug; 411 | }; 412 | F925FC3D2098A50F000134F0 /* Release */ = { 413 | isa = XCBuildConfiguration; 414 | buildSettings = { 415 | ALWAYS_SEARCH_USER_PATHS = NO; 416 | CLANG_ANALYZER_NONNULL = YES; 417 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 418 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 419 | CLANG_CXX_LIBRARY = "libc++"; 420 | CLANG_ENABLE_MODULES = YES; 421 | CLANG_ENABLE_OBJC_ARC = YES; 422 | CLANG_ENABLE_OBJC_WEAK = YES; 423 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 424 | CLANG_WARN_BOOL_CONVERSION = YES; 425 | CLANG_WARN_COMMA = YES; 426 | CLANG_WARN_CONSTANT_CONVERSION = YES; 427 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 428 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 429 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 430 | CLANG_WARN_EMPTY_BODY = YES; 431 | CLANG_WARN_ENUM_CONVERSION = YES; 432 | CLANG_WARN_INFINITE_RECURSION = YES; 433 | CLANG_WARN_INT_CONVERSION = YES; 434 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 435 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 436 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 437 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 438 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 439 | CLANG_WARN_STRICT_PROTOTYPES = YES; 440 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 441 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 442 | CLANG_WARN_UNREACHABLE_CODE = YES; 443 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 444 | CODE_SIGN_IDENTITY = "iPhone Developer"; 445 | COPY_PHASE_STRIP = NO; 446 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 447 | ENABLE_NS_ASSERTIONS = NO; 448 | ENABLE_STRICT_OBJC_MSGSEND = YES; 449 | GCC_C_LANGUAGE_STANDARD = gnu11; 450 | GCC_NO_COMMON_BLOCKS = YES; 451 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 452 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 453 | GCC_WARN_UNDECLARED_SELECTOR = YES; 454 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 455 | GCC_WARN_UNUSED_FUNCTION = YES; 456 | GCC_WARN_UNUSED_VARIABLE = YES; 457 | IPHONEOS_DEPLOYMENT_TARGET = 11.3; 458 | MTL_ENABLE_DEBUG_INFO = NO; 459 | SDKROOT = iphoneos; 460 | SWIFT_COMPILATION_MODE = wholemodule; 461 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 462 | VALIDATE_PRODUCT = YES; 463 | }; 464 | name = Release; 465 | }; 466 | F925FC3F2098A50F000134F0 /* Debug */ = { 467 | isa = XCBuildConfiguration; 468 | buildSettings = { 469 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 470 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 471 | CODE_SIGN_STYLE = Automatic; 472 | DEVELOPMENT_TEAM = W29774Y62S; 473 | INFOPLIST_FILE = "MGRS Converter/Info.plist"; 474 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 475 | LD_RUNPATH_SEARCH_PATHS = ( 476 | "$(inherited)", 477 | "@executable_path/Frameworks", 478 | ); 479 | PRODUCT_BUNDLE_IDENTIFIER = "com.BlackSmithDevelopers.MGRS-Converter"; 480 | PRODUCT_NAME = "$(TARGET_NAME)"; 481 | SWIFT_VERSION = 4.0; 482 | TARGETED_DEVICE_FAMILY = 2; 483 | }; 484 | name = Debug; 485 | }; 486 | F925FC402098A50F000134F0 /* Release */ = { 487 | isa = XCBuildConfiguration; 488 | buildSettings = { 489 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 490 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 491 | CODE_SIGN_STYLE = Automatic; 492 | DEVELOPMENT_TEAM = W29774Y62S; 493 | INFOPLIST_FILE = "MGRS Converter/Info.plist"; 494 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 495 | LD_RUNPATH_SEARCH_PATHS = ( 496 | "$(inherited)", 497 | "@executable_path/Frameworks", 498 | ); 499 | PRODUCT_BUNDLE_IDENTIFIER = "com.BlackSmithDevelopers.MGRS-Converter"; 500 | PRODUCT_NAME = "$(TARGET_NAME)"; 501 | SWIFT_VERSION = 4.0; 502 | TARGETED_DEVICE_FAMILY = 2; 503 | }; 504 | name = Release; 505 | }; 506 | F981FEA32113BE3E0089A553 /* Debug */ = { 507 | isa = XCBuildConfiguration; 508 | buildSettings = { 509 | CODE_SIGN_IDENTITY = ""; 510 | CODE_SIGN_STYLE = Automatic; 511 | CURRENT_PROJECT_VERSION = 1; 512 | DEFINES_MODULE = YES; 513 | DEVELOPMENT_TEAM = W29774Y62S; 514 | DYLIB_COMPATIBILITY_VERSION = 1; 515 | DYLIB_CURRENT_VERSION = 1; 516 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 517 | INFOPLIST_FILE = Coordinates/Info.plist; 518 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 519 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 520 | LD_RUNPATH_SEARCH_PATHS = ( 521 | "$(inherited)", 522 | "@executable_path/Frameworks", 523 | "@loader_path/Frameworks", 524 | ); 525 | PRODUCT_BUNDLE_IDENTIFIER = com.BlackSmithDevelopers.Coordinates; 526 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 527 | SKIP_INSTALL = YES; 528 | SWIFT_VERSION = 4.0; 529 | TARGETED_DEVICE_FAMILY = "1,2"; 530 | VERSIONING_SYSTEM = "apple-generic"; 531 | VERSION_INFO_PREFIX = ""; 532 | }; 533 | name = Debug; 534 | }; 535 | F981FEA42113BE3E0089A553 /* Release */ = { 536 | isa = XCBuildConfiguration; 537 | buildSettings = { 538 | CODE_SIGN_IDENTITY = ""; 539 | CODE_SIGN_STYLE = Automatic; 540 | CURRENT_PROJECT_VERSION = 1; 541 | DEFINES_MODULE = YES; 542 | DEVELOPMENT_TEAM = W29774Y62S; 543 | DYLIB_COMPATIBILITY_VERSION = 1; 544 | DYLIB_CURRENT_VERSION = 1; 545 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 546 | INFOPLIST_FILE = Coordinates/Info.plist; 547 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 548 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 549 | LD_RUNPATH_SEARCH_PATHS = ( 550 | "$(inherited)", 551 | "@executable_path/Frameworks", 552 | "@loader_path/Frameworks", 553 | ); 554 | PRODUCT_BUNDLE_IDENTIFIER = com.BlackSmithDevelopers.Coordinates; 555 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 556 | SKIP_INSTALL = YES; 557 | SWIFT_VERSION = 4.0; 558 | TARGETED_DEVICE_FAMILY = "1,2"; 559 | VERSIONING_SYSTEM = "apple-generic"; 560 | VERSION_INFO_PREFIX = ""; 561 | }; 562 | name = Release; 563 | }; 564 | /* End XCBuildConfiguration section */ 565 | 566 | /* Begin XCConfigurationList section */ 567 | F925FC272098A50E000134F0 /* Build configuration list for PBXProject "MGRS Converter" */ = { 568 | isa = XCConfigurationList; 569 | buildConfigurations = ( 570 | F925FC3C2098A50F000134F0 /* Debug */, 571 | F925FC3D2098A50F000134F0 /* Release */, 572 | ); 573 | defaultConfigurationIsVisible = 0; 574 | defaultConfigurationName = Release; 575 | }; 576 | F925FC3E2098A50F000134F0 /* Build configuration list for PBXNativeTarget "MGRS Converter" */ = { 577 | isa = XCConfigurationList; 578 | buildConfigurations = ( 579 | F925FC3F2098A50F000134F0 /* Debug */, 580 | F925FC402098A50F000134F0 /* Release */, 581 | ); 582 | defaultConfigurationIsVisible = 0; 583 | defaultConfigurationName = Release; 584 | }; 585 | F981FEA52113BE3E0089A553 /* Build configuration list for PBXNativeTarget "Coordinates" */ = { 586 | isa = XCConfigurationList; 587 | buildConfigurations = ( 588 | F981FEA32113BE3E0089A553 /* Debug */, 589 | F981FEA42113BE3E0089A553 /* Release */, 590 | ); 591 | defaultConfigurationIsVisible = 0; 592 | defaultConfigurationName = Release; 593 | }; 594 | /* End XCConfigurationList section */ 595 | }; 596 | rootObject = F925FC242098A50E000134F0 /* Project object */; 597 | } 598 | -------------------------------------------------------------------------------- /MGRS Converter.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /MGRS Converter.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /MGRS Converter.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /MGRS Converter.xcodeproj/project.xcworkspace/xcuserdata/johnayers.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blacksmithdevelopers/MGRSConverter/df31937b31235f38f49bbb19ef2bcf05f88c5501/MGRS Converter.xcodeproj/project.xcworkspace/xcuserdata/johnayers.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /MGRS Converter.xcodeproj/project.xcworkspace/xcuserdata/johnayers.xcuserdatad/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | BuildLocationStyle 6 | UseAppPreferences 7 | CustomBuildLocationType 8 | RelativeToDerivedData 9 | DerivedDataLocationStyle 10 | Default 11 | EnabledFullIndexStoreVisibility 12 | 13 | IssueFilterStyle 14 | ShowActiveSchemeOnly 15 | LiveSourceIssuesEnabled 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /MGRS Converter.xcodeproj/xcuserdata/johnayers.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /MGRS Converter.xcodeproj/xcuserdata/johnayers.xcuserdatad/xcschemes/MGRS Converter.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 69 | 70 | 71 | 72 | 73 | 74 | 80 | 82 | 88 | 89 | 90 | 91 | 93 | 94 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /MGRS Converter.xcodeproj/xcuserdata/johnayers.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Coordinates.xcscheme 8 | 9 | orderHint 10 | 1 11 | 12 | MGRS Converter.xcscheme 13 | 14 | orderHint 15 | 0 16 | 17 | 18 | SuppressBuildableAutocreation 19 | 20 | F925FC2B2098A50E000134F0 21 | 22 | primary 23 | 24 | 25 | F981FE992113BE3E0089A553 26 | 27 | primary 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /MGRS Converter/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // MGRS Converter 4 | // 5 | // Created by John Ayers on 5/1/18. 6 | // Copyright © 2018 Blacksmith Developers. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import MapKit 11 | 12 | @UIApplicationMain 13 | class AppDelegate: UIResponder, UIApplicationDelegate { 14 | 15 | var window: UIWindow? 16 | var sentCoordinates: Bool = false 17 | var coordinates: CLLocationCoordinate2D? 18 | 19 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 20 | // Override point for customization after application launch. 21 | return true 22 | } 23 | 24 | func applicationWillResignActive(_ application: UIApplication) { 25 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 26 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 27 | } 28 | 29 | func applicationDidEnterBackground(_ application: UIApplication) { 30 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 31 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 32 | } 33 | 34 | func applicationWillEnterForeground(_ application: UIApplication) { 35 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 36 | } 37 | 38 | func applicationDidBecomeActive(_ application: UIApplication) { 39 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 40 | } 41 | 42 | func applicationWillTerminate(_ application: UIApplication) { 43 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 44 | } 45 | 46 | 47 | } 48 | 49 | -------------------------------------------------------------------------------- /MGRS Converter/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "57x57", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-57x57@1x.png", 49 | "scale" : "1x" 50 | }, 51 | { 52 | "size" : "57x57", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-57x57@2x.png", 55 | "scale" : "2x" 56 | }, 57 | { 58 | "size" : "60x60", 59 | "idiom" : "iphone", 60 | "filename" : "Icon-App-60x60@2x.png", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "size" : "60x60", 65 | "idiom" : "iphone", 66 | "filename" : "Icon-App-60x60@3x.png", 67 | "scale" : "3x" 68 | }, 69 | { 70 | "size" : "20x20", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-20x20@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "20x20", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-20x20@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "29x29", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-29x29@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "29x29", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-29x29@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "40x40", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-40x40@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "40x40", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-40x40@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "50x50", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-Small-50x50@1x.png", 109 | "scale" : "1x" 110 | }, 111 | { 112 | "size" : "50x50", 113 | "idiom" : "ipad", 114 | "filename" : "Icon-Small-50x50@2x.png", 115 | "scale" : "2x" 116 | }, 117 | { 118 | "size" : "72x72", 119 | "idiom" : "ipad", 120 | "filename" : "Icon-App-72x72@1x.png", 121 | "scale" : "1x" 122 | }, 123 | { 124 | "size" : "72x72", 125 | "idiom" : "ipad", 126 | "filename" : "Icon-App-72x72@2x.png", 127 | "scale" : "2x" 128 | }, 129 | { 130 | "size" : "76x76", 131 | "idiom" : "ipad", 132 | "filename" : "Icon-App-76x76@1x.png", 133 | "scale" : "1x" 134 | }, 135 | { 136 | "size" : "76x76", 137 | "idiom" : "ipad", 138 | "filename" : "Icon-App-76x76@2x.png", 139 | "scale" : "2x" 140 | }, 141 | { 142 | "size" : "83.5x83.5", 143 | "idiom" : "ipad", 144 | "filename" : "Icon-App-83.5x83.5@2x.png", 145 | "scale" : "2x" 146 | }, 147 | { 148 | "size" : "1024x1024", 149 | "idiom" : "ios-marketing", 150 | "filename" : "ItunesArtwork@2x.png", 151 | "scale" : "1x" 152 | } 153 | ], 154 | "info" : { 155 | "version" : 1, 156 | "author" : "xcode" 157 | } 158 | } -------------------------------------------------------------------------------- /MGRS Converter/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blacksmithdevelopers/MGRSConverter/df31937b31235f38f49bbb19ef2bcf05f88c5501/MGRS Converter/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /MGRS Converter/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blacksmithdevelopers/MGRSConverter/df31937b31235f38f49bbb19ef2bcf05f88c5501/MGRS Converter/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /MGRS Converter/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blacksmithdevelopers/MGRSConverter/df31937b31235f38f49bbb19ef2bcf05f88c5501/MGRS Converter/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /MGRS Converter/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blacksmithdevelopers/MGRSConverter/df31937b31235f38f49bbb19ef2bcf05f88c5501/MGRS Converter/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /MGRS Converter/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blacksmithdevelopers/MGRSConverter/df31937b31235f38f49bbb19ef2bcf05f88c5501/MGRS Converter/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /MGRS Converter/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blacksmithdevelopers/MGRSConverter/df31937b31235f38f49bbb19ef2bcf05f88c5501/MGRS Converter/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /MGRS Converter/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blacksmithdevelopers/MGRSConverter/df31937b31235f38f49bbb19ef2bcf05f88c5501/MGRS Converter/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /MGRS Converter/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blacksmithdevelopers/MGRSConverter/df31937b31235f38f49bbb19ef2bcf05f88c5501/MGRS Converter/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /MGRS Converter/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blacksmithdevelopers/MGRSConverter/df31937b31235f38f49bbb19ef2bcf05f88c5501/MGRS Converter/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /MGRS Converter/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blacksmithdevelopers/MGRSConverter/df31937b31235f38f49bbb19ef2bcf05f88c5501/MGRS Converter/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png -------------------------------------------------------------------------------- /MGRS Converter/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blacksmithdevelopers/MGRSConverter/df31937b31235f38f49bbb19ef2bcf05f88c5501/MGRS Converter/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png -------------------------------------------------------------------------------- /MGRS Converter/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blacksmithdevelopers/MGRSConverter/df31937b31235f38f49bbb19ef2bcf05f88c5501/MGRS Converter/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /MGRS Converter/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blacksmithdevelopers/MGRSConverter/df31937b31235f38f49bbb19ef2bcf05f88c5501/MGRS Converter/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /MGRS Converter/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blacksmithdevelopers/MGRSConverter/df31937b31235f38f49bbb19ef2bcf05f88c5501/MGRS Converter/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png -------------------------------------------------------------------------------- /MGRS Converter/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blacksmithdevelopers/MGRSConverter/df31937b31235f38f49bbb19ef2bcf05f88c5501/MGRS Converter/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png -------------------------------------------------------------------------------- /MGRS Converter/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blacksmithdevelopers/MGRSConverter/df31937b31235f38f49bbb19ef2bcf05f88c5501/MGRS Converter/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /MGRS Converter/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blacksmithdevelopers/MGRSConverter/df31937b31235f38f49bbb19ef2bcf05f88c5501/MGRS Converter/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /MGRS Converter/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blacksmithdevelopers/MGRSConverter/df31937b31235f38f49bbb19ef2bcf05f88c5501/MGRS Converter/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /MGRS Converter/Assets.xcassets/AppIcon.appiconset/Icon-Small-50x50@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blacksmithdevelopers/MGRSConverter/df31937b31235f38f49bbb19ef2bcf05f88c5501/MGRS Converter/Assets.xcassets/AppIcon.appiconset/Icon-Small-50x50@1x.png -------------------------------------------------------------------------------- /MGRS Converter/Assets.xcassets/AppIcon.appiconset/Icon-Small-50x50@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blacksmithdevelopers/MGRSConverter/df31937b31235f38f49bbb19ef2bcf05f88c5501/MGRS Converter/Assets.xcassets/AppIcon.appiconset/Icon-Small-50x50@2x.png -------------------------------------------------------------------------------- /MGRS Converter/Assets.xcassets/AppIcon.appiconset/ItunesArtwork@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blacksmithdevelopers/MGRSConverter/df31937b31235f38f49bbb19ef2bcf05f88c5501/MGRS Converter/Assets.xcassets/AppIcon.appiconset/ItunesArtwork@2x.png -------------------------------------------------------------------------------- /MGRS Converter/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /MGRS Converter/Assets.xcassets/calculator_icon.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "icons8-calculator.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "icons8-calculator-1.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "icons8-calculator-2.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /MGRS Converter/Assets.xcassets/calculator_icon.imageset/icons8-calculator-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blacksmithdevelopers/MGRSConverter/df31937b31235f38f49bbb19ef2bcf05f88c5501/MGRS Converter/Assets.xcassets/calculator_icon.imageset/icons8-calculator-1.png -------------------------------------------------------------------------------- /MGRS Converter/Assets.xcassets/calculator_icon.imageset/icons8-calculator-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blacksmithdevelopers/MGRSConverter/df31937b31235f38f49bbb19ef2bcf05f88c5501/MGRS Converter/Assets.xcassets/calculator_icon.imageset/icons8-calculator-2.png -------------------------------------------------------------------------------- /MGRS Converter/Assets.xcassets/calculator_icon.imageset/icons8-calculator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blacksmithdevelopers/MGRSConverter/df31937b31235f38f49bbb19ef2bcf05f88c5501/MGRS Converter/Assets.xcassets/calculator_icon.imageset/icons8-calculator.png -------------------------------------------------------------------------------- /MGRS Converter/Assets.xcassets/icons8-cancel_filled.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "icons8-cancel_filled-2.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "icons8-cancel_filled-1.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "icons8-cancel_filled.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /MGRS Converter/Assets.xcassets/icons8-cancel_filled.imageset/icons8-cancel_filled-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blacksmithdevelopers/MGRSConverter/df31937b31235f38f49bbb19ef2bcf05f88c5501/MGRS Converter/Assets.xcassets/icons8-cancel_filled.imageset/icons8-cancel_filled-1.png -------------------------------------------------------------------------------- /MGRS Converter/Assets.xcassets/icons8-cancel_filled.imageset/icons8-cancel_filled-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blacksmithdevelopers/MGRSConverter/df31937b31235f38f49bbb19ef2bcf05f88c5501/MGRS Converter/Assets.xcassets/icons8-cancel_filled.imageset/icons8-cancel_filled-2.png -------------------------------------------------------------------------------- /MGRS Converter/Assets.xcassets/icons8-cancel_filled.imageset/icons8-cancel_filled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blacksmithdevelopers/MGRSConverter/df31937b31235f38f49bbb19ef2bcf05f88c5501/MGRS Converter/Assets.xcassets/icons8-cancel_filled.imageset/icons8-cancel_filled.png -------------------------------------------------------------------------------- /MGRS Converter/Assets.xcassets/icons8-map_editing-1.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "icons8-map_editing.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "icons8-map_editing-2.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "icons8-map_editing-1.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /MGRS Converter/Assets.xcassets/icons8-map_editing-1.imageset/icons8-map_editing-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blacksmithdevelopers/MGRSConverter/df31937b31235f38f49bbb19ef2bcf05f88c5501/MGRS Converter/Assets.xcassets/icons8-map_editing-1.imageset/icons8-map_editing-1.png -------------------------------------------------------------------------------- /MGRS Converter/Assets.xcassets/icons8-map_editing-1.imageset/icons8-map_editing-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blacksmithdevelopers/MGRSConverter/df31937b31235f38f49bbb19ef2bcf05f88c5501/MGRS Converter/Assets.xcassets/icons8-map_editing-1.imageset/icons8-map_editing-2.png -------------------------------------------------------------------------------- /MGRS Converter/Assets.xcassets/icons8-map_editing-1.imageset/icons8-map_editing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blacksmithdevelopers/MGRSConverter/df31937b31235f38f49bbb19ef2bcf05f88c5501/MGRS Converter/Assets.xcassets/icons8-map_editing-1.imageset/icons8-map_editing.png -------------------------------------------------------------------------------- /MGRS Converter/Assets.xcassets/icons8-map_pin.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "icons8-map_pin-2.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "icons8-map_pin-1.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "icons8-map_pin.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /MGRS Converter/Assets.xcassets/icons8-map_pin.imageset/icons8-map_pin-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blacksmithdevelopers/MGRSConverter/df31937b31235f38f49bbb19ef2bcf05f88c5501/MGRS Converter/Assets.xcassets/icons8-map_pin.imageset/icons8-map_pin-1.png -------------------------------------------------------------------------------- /MGRS Converter/Assets.xcassets/icons8-map_pin.imageset/icons8-map_pin-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blacksmithdevelopers/MGRSConverter/df31937b31235f38f49bbb19ef2bcf05f88c5501/MGRS Converter/Assets.xcassets/icons8-map_pin.imageset/icons8-map_pin-2.png -------------------------------------------------------------------------------- /MGRS Converter/Assets.xcassets/icons8-map_pin.imageset/icons8-map_pin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blacksmithdevelopers/MGRSConverter/df31937b31235f38f49bbb19ef2bcf05f88c5501/MGRS Converter/Assets.xcassets/icons8-map_pin.imageset/icons8-map_pin.png -------------------------------------------------------------------------------- /MGRS Converter/Assets.xcassets/icons8-street_view_filled.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "icons8-street_view_filled.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "icons8-street_view_filled-1.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "icons8-street_view_filled-2.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /MGRS Converter/Assets.xcassets/icons8-street_view_filled.imageset/icons8-street_view_filled-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blacksmithdevelopers/MGRSConverter/df31937b31235f38f49bbb19ef2bcf05f88c5501/MGRS Converter/Assets.xcassets/icons8-street_view_filled.imageset/icons8-street_view_filled-1.png -------------------------------------------------------------------------------- /MGRS Converter/Assets.xcassets/icons8-street_view_filled.imageset/icons8-street_view_filled-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blacksmithdevelopers/MGRSConverter/df31937b31235f38f49bbb19ef2bcf05f88c5501/MGRS Converter/Assets.xcassets/icons8-street_view_filled.imageset/icons8-street_view_filled-2.png -------------------------------------------------------------------------------- /MGRS Converter/Assets.xcassets/icons8-street_view_filled.imageset/icons8-street_view_filled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blacksmithdevelopers/MGRSConverter/df31937b31235f38f49bbb19ef2bcf05f88c5501/MGRS Converter/Assets.xcassets/icons8-street_view_filled.imageset/icons8-street_view_filled.png -------------------------------------------------------------------------------- /MGRS Converter/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /MGRS Converter/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 57 | 63 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 115 | 121 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 156 | 162 | 168 | 174 | 175 | 176 | 191 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 267 | 274 | 286 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | -------------------------------------------------------------------------------- /MGRS Converter/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleDisplayName 8 | MGRS Converter 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | NSLocationUsageDescription 26 | This application uses your location to show your position on the map 27 | NSLocationWhenInUseUsageDescription 28 | This application is using your location to show your position on th map 29 | UILaunchStoryboardName 30 | LaunchScreen 31 | UIMainStoryboardFile 32 | Main 33 | UIRequiredDeviceCapabilities 34 | 35 | armv7 36 | 37 | UISupportedInterfaceOrientations 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationLandscapeLeft 41 | UIInterfaceOrientationLandscapeRight 42 | 43 | UISupportedInterfaceOrientations~ipad 44 | 45 | UIInterfaceOrientationPortrait 46 | UIInterfaceOrientationPortraitUpsideDown 47 | UIInterfaceOrientationLandscapeLeft 48 | UIInterfaceOrientationLandscapeRight 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /MGRS Converter/MapViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MapViewController.swift 3 | // MGRS Converter 4 | // 5 | // Created by John Ayers on 5/4/18. 6 | // Copyright © 2018 Blacksmith Developers. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import MapKit 11 | import Coordinates 12 | 13 | class MapViewController: UIViewController, UIGestureRecognizerDelegate, MKMapViewDelegate { 14 | 15 | @IBOutlet var mapView: MKMapView! 16 | @IBOutlet var mgrsLabel: UILabel! 17 | @IBOutlet var latLonLabel: UILabel! 18 | @IBOutlet var showLocationButton: UIButton! 19 | @IBOutlet var clearMapButton: UIButton! 20 | // var locationManager: CLLocationManager! 21 | 22 | let mapPin = MKPointAnnotation() 23 | 24 | override func loadView() { 25 | super.loadView() 26 | 27 | // locationManager = CLLocationManager() 28 | 29 | mapView.mapType = .hybrid 30 | let segmentedControl = UISegmentedControl(items: ["Standard", "Satellite", "Hybrid"]) 31 | segmentedControl.backgroundColor = UIColor.white.withAlphaComponent(0.5) 32 | segmentedControl.selectedSegmentIndex = 2 33 | segmentedControl.translatesAutoresizingMaskIntoConstraints = false 34 | mapView.addSubview(segmentedControl) 35 | 36 | let topConstraint = segmentedControl.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 8) 37 | let margins = view.layoutMarginsGuide 38 | let leadingConstraint = segmentedControl.leadingAnchor.constraint(equalTo: margins.leadingAnchor) 39 | let trailingConstraint = segmentedControl.trailingAnchor.constraint(equalTo: margins.trailingAnchor) 40 | topConstraint.isActive = true 41 | leadingConstraint.isActive = true 42 | trailingConstraint.isActive = true 43 | 44 | segmentedControl.addTarget(self, action: #selector(MapViewController.mapTypeChanged(_:)), for: .valueChanged) 45 | 46 | latLonLabel.alpha = 0.0 47 | mgrsLabel.alpha = 0.0 48 | 49 | showLocationButton.layer.cornerRadius = 0.5 * showLocationButton.bounds.size.width 50 | showLocationButton.clipsToBounds = true 51 | 52 | clearMapButton.layer.cornerRadius = 0.5 * clearMapButton.bounds.size.width 53 | clearMapButton.clipsToBounds = true 54 | } 55 | 56 | override func viewDidAppear(_ animated: Bool) { 57 | super.viewDidAppear(animated) 58 | let appDelegate = UIApplication.shared.delegate as! AppDelegate 59 | if appDelegate.sentCoordinates{ 60 | if let coords = appDelegate.coordinates { 61 | coordinatesRecieved(coordinates: coords) 62 | } 63 | appDelegate.sentCoordinates = false 64 | appDelegate.coordinates = nil 65 | } 66 | } 67 | 68 | func mapView(_ mapView: MKMapView, didUpdate userLocation: MKUserLocation) { 69 | let coord = userLocation.coordinate 70 | let region = MKCoordinateRegion(center: coord, span: MKCoordinateSpan(latitudeDelta: 0.01, longitudeDelta: 0.01)) 71 | mapView.setRegion(region, animated: true) 72 | updateLabels(lat: coord.latitude, lon: coord.longitude) 73 | mapView.removeAnnotation(mapPin) 74 | } 75 | 76 | @IBAction func centerViewOnUserPosition(_ sender: UIButton) { 77 | // locationManager.startUpdatingLocation() 78 | mapView.showsUserLocation = true 79 | } 80 | 81 | @IBAction func clearMapMarks(_ sender: UIButton) { 82 | mapView.showsUserLocation = false 83 | mapView.removeAnnotation(mapPin) 84 | } 85 | 86 | @objc func mapTypeChanged(_ segControl: UISegmentedControl){ 87 | mapView.mapType = MKMapType.init(rawValue: UInt(segControl.selectedSegmentIndex)) ?? .standard 88 | } 89 | 90 | @IBAction func captureUserPressedLocation(sender: UILongPressGestureRecognizer){ 91 | if sender.state != UIGestureRecognizerState.began {return} 92 | mapView.showsUserLocation = false 93 | let touchLoc = sender.location(in: mapView) 94 | let locCoord = mapView.convert(touchLoc, toCoordinateFrom: mapView) 95 | // print("Tapped \(locCoord.latitude), \(locCoord.longitude)") 96 | coordinatesRecieved(coordinates: locCoord) 97 | } 98 | 99 | func coordinatesRecieved(coordinates: CLLocationCoordinate2D) 100 | { 101 | let locCoord = coordinates 102 | updateLabels(lat: locCoord.latitude, lon: locCoord.longitude) 103 | let region = MKCoordinateRegion(center: locCoord, span: MKCoordinateSpan(latitudeDelta: 0.01, longitudeDelta: 0.01)) 104 | mapView.setRegion(region, animated: true) 105 | mapView.removeAnnotation(mapPin) 106 | mapPin.coordinate = locCoord 107 | mapView.addAnnotation(mapPin) 108 | } 109 | 110 | func updateLabels(lat: Double, lon: Double){ 111 | 112 | let latLon = LatLon(lat: lat, lon: lon) 113 | latLonLabel.alpha = 0.5 114 | latLonLabel.text = "Lat/Lon:\n\(latLon.toString(format: DMSFormat.degreesMinutesSeconds, decimalPlaces: 2, newLinesForEachCoord: true))" 115 | do{ 116 | let mgrs = try latLon.toUTM().toMGRS() 117 | mgrsLabel.alpha = 0.5 118 | mgrsLabel.text = "MGRS: \n\(mgrs.toString())" 119 | } 120 | catch { 121 | return 122 | } 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /MGRS Converter/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // MGRS Converter 4 | // 5 | // Created by John Ayers on 5/1/18. 6 | // Copyright © 2018 Blacksmith Developers. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import MapKit 11 | import Coordinates 12 | 13 | class ViewController: UIViewController, UITextFieldDelegate, UIGestureRecognizerDelegate { 14 | 15 | @IBOutlet var mgrsCoordsEnteredLabel: UILabel! 16 | @IBOutlet var mgrsCoordsDisplayedLabel: UILabel! 17 | @IBOutlet var latlonCoordsEnteredLabel: UILabel! 18 | @IBOutlet var latlonCoordsDisplayedLabel: UILabel! 19 | 20 | @IBOutlet var mgrsEnteredText: UITextField! 21 | @IBOutlet var latEnteredText: UITextField! 22 | @IBOutlet var lonEnteredText: UITextField! 23 | 24 | @IBOutlet var conversionLabel: UILabel! 25 | @IBOutlet var conversionDisplay: UILabel! 26 | @IBOutlet var newCoordsLabel: UILabel! 27 | @IBOutlet var newCoordsDisplay: UILabel! 28 | 29 | @IBOutlet var clearButton: UIButton! 30 | @IBOutlet var seeOnMapButton: UIButton! 31 | 32 | var storedLatLon: LatLon? 33 | 34 | @IBAction func convertToLatLon() { 35 | if let mgrsEntered = mgrsEnteredText.text, mgrsEntered.count > 0 { 36 | do{ 37 | let mgrs = try Mgrs.parse(fromString: mgrsEntered) 38 | let utm = try mgrs.toUTM() 39 | let latLon = utm.toLatLonE() 40 | 41 | latlonCoordsEnteredLabel.alpha = 0.0 42 | latlonCoordsDisplayedLabel.alpha = 0.0 43 | mgrsCoordsEnteredLabel.alpha = 1.0 44 | mgrsCoordsDisplayedLabel.alpha = 1.0 45 | mgrsCoordsDisplayedLabel.text = mgrs.toString(precision: 5) 46 | 47 | conversionLabel.alpha = 1.0 48 | conversionDisplay.alpha = 1.0 49 | conversionDisplay.text = "MGRS --> Lat/Lon" 50 | newCoordsLabel.alpha = 1.0 51 | newCoordsDisplay.alpha = 1.0 52 | newCoordsDisplay.text = latLon.toString(format: .degreesMinutesSeconds, decimalPlaces: 4) 53 | clearButton.alpha = 1.0 54 | seeOnMapButton.alpha = 1.0 55 | mgrsEnteredText.text = "" 56 | 57 | storedLatLon = latLon 58 | view.endEditing(true) 59 | } 60 | catch MgrsError.invalidFormat(let format) { 61 | displayAlertController(msg: format, thrower: .mgrs) 62 | return 63 | } 64 | catch MgrsError.invalidGrid(let grid) { 65 | displayAlertController(msg: grid, thrower: .mgrs) 66 | return 67 | } 68 | catch MgrsError.invalidZone(let zone) { 69 | displayAlertController(msg: zone, thrower: .mgrs) 70 | return 71 | } 72 | catch MgrsError.invalidBand(let band) { 73 | displayAlertController(msg: band, thrower: .mgrs) 74 | return 75 | } 76 | catch MgrsError.invalidEasting(let easting) { 77 | displayAlertController(msg: easting, thrower: .mgrs) 78 | return 79 | } 80 | catch MgrsError.invalidNorthing(let northing) { 81 | displayAlertController(msg: northing, thrower: .mgrs) 82 | return 83 | } 84 | catch UtmError.badLatLon(let err) { 85 | displayAlertController(msg: err, thrower: .utm ) 86 | return 87 | } 88 | catch UtmError.invalidEasting(let easting) { 89 | displayAlertController(msg: easting, thrower: .utm) 90 | return 91 | } 92 | catch UtmError.invalidNorthing(let northing) { 93 | displayAlertController(msg: northing, thrower: .utm) 94 | return 95 | } 96 | catch UtmError.invalidZone(let zone) { 97 | displayAlertController(msg: zone, thrower: .utm) 98 | return 99 | } 100 | catch { 101 | displayAlertController(msg: "Unknown error occurred", thrower: .unk ) 102 | return 103 | } 104 | } else { 105 | displayAlertController(msg: "Invalid MGRS Entered", thrower: .unk) 106 | } 107 | } 108 | @IBAction func convertToMGRS() { 109 | if let latEntered = latEnteredText.text, latEntered.count > 0, let lonEntered = lonEnteredText.text, lonEntered.count > 0 { 110 | do{ 111 | let latLon = try LatLon.parseLatLon(stringToParse: "\(latEntered), \(lonEntered)") 112 | let utm = try latLon.toUTM() 113 | let mgrs = try utm.toMGRS() 114 | 115 | mgrsCoordsEnteredLabel.alpha = 0.0 116 | mgrsCoordsDisplayedLabel.alpha = 0.0 117 | latlonCoordsEnteredLabel.alpha = 1.0 118 | latlonCoordsDisplayedLabel.alpha = 1.0 119 | 120 | latlonCoordsDisplayedLabel.text = latLon.toString(format: .degreesMinutesSeconds, decimalPlaces: 4) 121 | 122 | conversionLabel.alpha = 1.0 123 | conversionDisplay.alpha = 1.0 124 | conversionDisplay.text = "Lat/Lon --> MGRS" 125 | newCoordsLabel.alpha = 1.0 126 | newCoordsDisplay.alpha = 1.0 127 | newCoordsDisplay.text = mgrs.toString(precision: 5) 128 | clearButton.alpha = 1.0 129 | seeOnMapButton.alpha = 1.0 130 | mgrsEnteredText.text = "" 131 | latEnteredText.text = "" 132 | lonEnteredText.text = "" 133 | storedLatLon = latLon 134 | view.endEditing(true) 135 | } 136 | catch LatLonError.parseError(let err){ 137 | displayAlertController(msg: err, thrower: .latLon ) 138 | return 139 | } 140 | catch UtmError.badLatLon(let err) { 141 | displayAlertController(msg: err, thrower: .utm ) 142 | return 143 | } 144 | catch UtmError.invalidEasting(let easting) { 145 | displayAlertController(msg: easting, thrower: .utm) 146 | return 147 | } 148 | catch UtmError.invalidNorthing(let northing) { 149 | displayAlertController(msg: northing, thrower: .utm) 150 | return 151 | } 152 | catch UtmError.invalidZone(let zone) { 153 | displayAlertController(msg: zone, thrower: .utm) 154 | return 155 | } 156 | catch MgrsError.invalidZone(let zone) { 157 | displayAlertController(msg: zone, thrower: .mgrs) 158 | return 159 | } 160 | catch MgrsError.invalidBand(let band) { 161 | displayAlertController(msg: band, thrower: .mgrs) 162 | return 163 | } 164 | catch { 165 | displayAlertController(msg: "Unknown error occurred", thrower: .unk ) 166 | return 167 | } 168 | 169 | } else { 170 | displayAlertController(msg: "Invalid Lat/Lon Entered", thrower: .unk) 171 | } 172 | } 173 | 174 | @IBAction func clearResults() { 175 | clearDisplay() 176 | } 177 | 178 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) { 179 | switch segue.identifier { 180 | case "seeCoordsOnMap"?: 181 | if let latLon = storedLatLon { 182 | let toSend = CLLocationCoordinate2D(latitude: latLon.lat, longitude: latLon.lon) 183 | let appDelegate = UIApplication.shared.delegate as! AppDelegate 184 | appDelegate.coordinates = toSend 185 | appDelegate.sentCoordinates = true 186 | 187 | self.tabBarController?.selectedIndex = 1 188 | } 189 | default: 190 | preconditionFailure("A segue was requested that does not exist") 191 | } 192 | } 193 | func textFieldShouldReturn(_ textField: UITextField) -> Bool { 194 | textField.resignFirstResponder() 195 | return true 196 | } 197 | 198 | @objc func backgroundTapped() { 199 | view.endEditing(true) 200 | } 201 | 202 | override func viewDidLoad() { 203 | super.viewDidLoad() 204 | // Do any additional setup after loading the view, typically from a nib. 205 | let tapGesture = UITapGestureRecognizer(target: self, action: #selector(backgroundTapped)) 206 | view.addGestureRecognizer(tapGesture) 207 | } 208 | 209 | override func viewWillAppear(_ animated: Bool) { 210 | clearDisplay() 211 | } 212 | 213 | override func viewWillDisappear(_ animated: Bool) { 214 | view.endEditing(true) 215 | } 216 | 217 | override func didReceiveMemoryWarning() { 218 | super.didReceiveMemoryWarning() 219 | // Dispose of any resources that can be recreated. 220 | } 221 | 222 | func clearDisplay(){ 223 | conversionLabel.alpha = 0.0 224 | conversionDisplay.alpha = 0.0 225 | newCoordsLabel.alpha = 0.0 226 | newCoordsDisplay.alpha = 0.0 227 | clearButton.alpha = 0.0 228 | seeOnMapButton.alpha = 0.0 229 | mgrsCoordsEnteredLabel.alpha = 0.0 230 | mgrsCoordsDisplayedLabel.alpha = 0.0 231 | latlonCoordsEnteredLabel.alpha = 0.0 232 | latlonCoordsDisplayedLabel.alpha = 0.0 233 | mgrsEnteredText.text = "" 234 | latEnteredText.text = "" 235 | lonEnteredText.text = "" 236 | storedLatLon = nil 237 | view.endEditing(true) 238 | } 239 | 240 | func displayAlertController(msg: String, thrower: errorOrigin){ 241 | let title: String 242 | switch thrower{ 243 | case .mgrs: 244 | title = "Error Converting to MGRS" 245 | case .utm: 246 | title = "Error Converting to UTM" 247 | case .latLon: 248 | title = "Error Converting to Lat Lon" 249 | case .unk: 250 | title = "Error" 251 | } 252 | let converterAction = UIAlertController(title: title, message: msg, preferredStyle: .alert) 253 | let okAction = UIAlertAction(title: "OK", style: .default){ _ in 254 | } 255 | converterAction.addAction(okAction) 256 | 257 | present(converterAction, animated: true, completion: nil) 258 | } 259 | 260 | enum errorOrigin: Error{ 261 | case mgrs 262 | case utm 263 | case latLon 264 | case unk 265 | } 266 | 267 | } 268 | 269 | -------------------------------------------------------------------------------- /MGRS Converter/globe.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blacksmithdevelopers/MGRSConverter/df31937b31235f38f49bbb19ef2bcf05f88c5501/MGRS Converter/globe.PNG -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MGRSConverter 2 | Converts between Lat/Lon and MGRS Coordinates 3 | 4 | The math and theory for the conversions between Latitude/Longitude, UTM, and MGRS was adapted from code provided by https://www.movable-type.co.uk/scripts/latlong-utm-mgrs.html 5 | The first unpublished iteration of this was a simple translation of the JavaScript provided to Swift. 6 | Subsequent iterations have evolved into packaging the math within into a more useable iOS interface. 7 | 8 | The next branch of this project will focus on offline map areas. 9 | --------------------------------------------------------------------------------