diff --git a/OpenAthenaIOS.xcodeproj/project.pbxproj b/OpenAthenaIOS.xcodeproj/project.pbxproj index 4ee1dc8..e592da7 100644 --- a/OpenAthenaIOS.xcodeproj/project.pbxproj +++ b/OpenAthenaIOS.xcodeproj/project.pbxproj @@ -708,7 +708,7 @@ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = OpenAthenaIOS/OpenAthenaIOS.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 842; + CURRENT_PROJECT_VERSION = 894; DEVELOPMENT_TEAM = S9737SV7NZ; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = OpenAthenaIOS/Info.plist; @@ -745,7 +745,7 @@ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = OpenAthenaIOS/OpenAthenaIOSRelease.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 842; + CURRENT_PROJECT_VERSION = 894; DEVELOPMENT_TEAM = S9737SV7NZ; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = OpenAthenaIOS/Info.plist; @@ -781,7 +781,7 @@ buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 842; + CURRENT_PROJECT_VERSION = 894; DEVELOPMENT_TEAM = S9737SV7NZ; GENERATE_INFOPLIST_FILE = YES; IPHONEOS_DEPLOYMENT_TARGET = 14.0; @@ -801,7 +801,7 @@ buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 842; + CURRENT_PROJECT_VERSION = 894; DEVELOPMENT_TEAM = S9737SV7NZ; GENERATE_INFOPLIST_FILE = YES; IPHONEOS_DEPLOYMENT_TARGET = 14.0; diff --git a/OpenAthenaIOS.xcworkspace/xcuserdata/rdk.xcuserdatad/UserInterfaceState.xcuserstate b/OpenAthenaIOS.xcworkspace/xcuserdata/rdk.xcuserdatad/UserInterfaceState.xcuserstate index 78dadef..8765dc7 100644 Binary files a/OpenAthenaIOS.xcworkspace/xcuserdata/rdk.xcuserdatad/UserInterfaceState.xcuserstate and b/OpenAthenaIOS.xcworkspace/xcuserdata/rdk.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/OpenAthenaIOS.xcworkspace/xcuserdata/rdk.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/OpenAthenaIOS.xcworkspace/xcuserdata/rdk.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist index 35b4777..016cdef 100644 --- a/OpenAthenaIOS.xcworkspace/xcuserdata/rdk.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +++ b/OpenAthenaIOS.xcworkspace/xcuserdata/rdk.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -14,8 +14,8 @@ filePath = "OpenAthenaIOS/DroneImage.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "1357" - endingLineNumber = "1357" + startingLineNumber = "1359" + endingLineNumber = "1359" landmarkName = "resolveTarget(dem:altReference:)" landmarkType = "7"> diff --git a/OpenAthenaIOS/DebugViewController.swift b/OpenAthenaIOS/DebugViewController.swift index 9f1f670..0422149 100644 --- a/OpenAthenaIOS/DebugViewController.swift +++ b/OpenAthenaIOS/DebugViewController.swift @@ -156,6 +156,9 @@ class DebugViewController: UIViewController, UIScrollViewDelegate { targetWidth: vc.theDroneImage!.theImage!.size.width) vc!.theDroneImage!.ccdInfo = ccdInfo foundCCDInfoString = "Found CCD info for drone make/model \(ccdInfo!.makeModel)
" + foundCCDInfoString += "Lens type: \(ccdInfo!.lensType)
" + foundCCDInfoString += "Comment: \(ccdInfo!.comment)
" + foundCCDInfoString += "Thermal?: \(ccdInfo!.isThermal)
" } catch { print("No CCD info for drone image, using estimates") @@ -249,51 +252,66 @@ class DebugViewController: UIViewController, UIScrollViewDelegate { self.htmlString += "
Altitude Data:
" do { let alt = try vc.theDroneImage!.getAltitude() + let altStr = roundDigitsToString(val: alt, precision: 0) if app.settings.unitsMode == .Metric { - self.htmlString += "Altitude: \(alt)m
" + self.htmlString += "Altitude: \(altStr)m
" } else { - self.htmlString += "Altitude: \(app.metersToFeet(meters: alt))ft
" + let altFt = app.metersToFeet(meters: alt) + let altFtStr = roundDigitsToString(val: altFt, precision: 0) + self.htmlString += "Altitude: \(altFtStr)ft
" } } catch { htmlString += "Altitude: missing
"} do { let relAlt = try vc.theDroneImage!.getRelativeAltitude() + let relAltStr = roundDigitsToString(val: relAlt, precision: 0) if app.settings.unitsMode == .Metric { - self.htmlString += "Relative altitude: \(relAlt)m
" + self.htmlString += "Relative altitude: \(relAltStr)m
" } else { - self.htmlString += "Relative altitude: \(app.metersToFeet(meters: relAlt))ft
" + let relAltFt = app.metersToFeet(meters: relAlt) + let relAltFtStr = roundDigitsToString(val: relAltFt, precision: 0) + self.htmlString += "Relative altitude: \( relAltFtStr)ft
" } } catch { self.htmlString += "Relative altitude: not preseent
" } do { let altFromRel = try vc.theDroneImage!.getAltitudeViaRelative(dem: vc.dem!) + let altFromRelStr = roundDigitsToString(val: altFromRel, precision: 0) if app.settings.unitsMode == .Metric { - self.htmlString += "Drone altitude via relative alt: \(altFromRel)m
" + self.htmlString += "Drone altitude via relative alt: \(altFromRelStr)m
" } else { - self.htmlString += "Drone altitude via relative alt: \(app.metersToFeet(meters: altFromRel))ft
" + let altFromRelFt = app.metersToFeet(meters: altFromRel) + let altFromRelFtStr = roundDigitsToString(val: altFromRelFt, precision: 0) + self.htmlString += "Drone altitude via relative alt: \(altFromRelStr)ft
" } } catch { self.htmlString += "Drone altitude via relative alt: unable to calculate
"} do { let altAboveGround = try vc.theDroneImage!.getAltitudeAboveGround() + let altAboveGroundStr = roundDigitsToString(val: altAboveGround, precision: 0) if app.settings.unitsMode == .Metric { - self.htmlString += "Altitude above ground: \(altAboveGround)m
" + self.htmlString += "Altitude above ground: \(altAboveGroundStr)m
" } else { - self.htmlString += "Altitude above ground: \(app.metersToFeet(meters: altAboveGround))ft
" + let altAboveGroundFt = app.metersToFeet(meters: altAboveGround) + let altAboveGroundFtStr = roundDigitsToString(val: altAboveGroundFt, precision: 0) + self.htmlString += "Altitude above ground: \(altAboveGroundStr)ft
" } } catch { self.htmlString += "Altitude above ground: not present
" } do { let altFromAboveGround = try vc.theDroneImage!.getAltitudeViaAboveGround(dem: vc.dem!) + let altFromAboveGroundStr = roundDigitsToString(val: altFromAboveGround, precision: 0) if app.settings.unitsMode == .Metric { - self.htmlString += "Drone altitude via above ground alt: \(altFromAboveGround)m
" + self.htmlString += "Drone altitude via above ground alt: \(altFromAboveGroundStr)m
" } else { - self.htmlString += "Drone altitude via above ground alt: \(app.metersToFeet(meters: altFromAboveGround))ft
" + let altFromAboveGroundFt = app.metersToFeet(meters: altFromAboveGround) + let altFromAboveGroundFtStr = roundDigitsToString(val: altFromAboveGroundFt, precision: 0) + self.htmlString += "Drone altitude via above ground alt: \(altFromAboveGroundFtStr))ft
" } } catch { diff --git a/OpenAthenaIOS/DroneImage.swift b/OpenAthenaIOS/DroneImage.swift index bbf1ae2..138f13d 100644 --- a/OpenAthenaIOS/DroneImage.swift +++ b/OpenAthenaIOS/DroneImage.swift @@ -103,8 +103,8 @@ public class DroneImage { let md = CGImageSourceCopyPropertiesAtIndex(src!,0,nil)! as NSDictionary let md2 = CGImageSourceCopyMetadataAtIndex(src!,0,nil) - print("updateMetaData: md is \(md)") - print("updateMetaData: md2 is \(md2)") + //print("updateMetaData: md is \(md)") + //print("updateMetaData: md2 is \(md2)") metaData = md.mutableCopy() as! NSMutableDictionary rawMetaData = md2 @@ -326,7 +326,7 @@ public class DroneImage { var alt = 0.0 var gpsInfo: NSDictionary - print("getAltitude: starting with 0.0") + //print("getAltitude: starting with 0.0") if metaData == nil { print("getAltitude: no metadata, bugging out") @@ -341,21 +341,21 @@ public class DroneImage { if metaData!["drone-dji:AbsoluteAltitude"] != nil { alt = (metaData!["drone-dji:AbsoluteAltitude"] as! NSString).doubleValue - print("getAltitude: drone-dji:AbsoluteAltitude \(alt)") + //print("getAltitude: drone-dji:AbsoluteAltitude \(alt)") //return alt } if metaData!["drone:AbsoluteAltitude"] != nil { alt = (metaData!["drone:AbsoluteAltitude"] as! NSString).doubleValue - print("getAltitude: drone:AbsoluteAltitude \(alt)") + //print("getAltitude: drone:AbsoluteAltitude \(alt)") //return alt } if metaData!["drone-skydio:AbsoluteAltitude"] != nil { alt = (metaData!["drone-skydio:AbsoluteAltitude"] as! NSString).doubleValue - print("getAltitude: drone-skydio:AbsoluteAltitude \(alt)") + //print("getAltitude: drone-skydio:AbsoluteAltitude \(alt)") //return alt } - print("getAltitude: \(alt), now going to make corrections") + //print("getAltitude: \(alt), now going to make corrections") // for parrot, what about Camera:AboveGroundAltitude XXX ? @@ -368,7 +368,7 @@ public class DroneImage { // if alt is still 0.0, grab here var altFromExif = false if alt == 0.0 { - print("getAltitude:0.0, falling back to GPS") + //print("getAltitude:0.0, falling back to GPS") if metaData!["{GPS}"] == nil { print("getAltitude: no gps meta data, bugging out") @@ -382,26 +382,26 @@ public class DroneImage { alt = gpsInfo["Altitude"] as! Double altFromExif = true - print("getAltitude: gps altitude is \(alt)") + //print("getAltitude: gps altitude is \(alt)") // re autel drones, check altitude ref for 1 meaning below sea level XXX if gpsInfo["GPSAltitudeRef"] != nil { let ref = gpsInfo["GPSAltitudeRef"] as! Int - print("getAltitude: GPSAltitude ref is \(ref)") + //print("getAltitude: GPSAltitude ref is \(ref)") if ref == 1 { alt = -1.0 * alt } } if gpsInfo["AltitudeRef"] != nil { let ref = gpsInfo["AltitudeRef"] as! Int - print("getAltitude Altitude ref is \(ref)") + //print("getAltitude Altitude ref is \(ref)") if ref == 1 { alt = -1.0 * alt } } if metaData!["exif:GPSAltitudeRef"] != nil { let ref = (metaData!["exif:GPSAltitudeRef"] as! NSString).intValue - print("getAltitude: GPS exif:GPSAltitudeRef is \(ref)") + //print("getAltitude: GPS exif:GPSAltitudeRef is \(ref)") if ref == 1 { alt = -1.0 * alt } @@ -413,7 +413,7 @@ public class DroneImage { throw DroneImageError.NoMetaGPSData } - print("getAltitude: alt is now \(alt)") + //print("getAltitude: alt is now \(alt)") // look for metadata drone:RtkAlt or drone:RtkAlt ? // despite RtkAlt, we still think DJI is reporting @@ -444,11 +444,11 @@ public class DroneImage { // drone-dji:AltitudeType=RtkAlt if xmlStringCopy?.lowercased().contains("rtkflag") == true { - print("getAltitude: xmlstringcopy rtkflag is true") + //print("getAltitude: xmlstringcopy rtkflag is true") rtkFlag = true } - print("getAltitude: rtkFlag is \(rtkFlag)") + //print("getAltitude: rtkFlag is \(rtkFlag)") // now, depending on drone type/make, convert from EGM96 to WGS84 if necessary var offset:Double = 0.0 @@ -462,7 +462,7 @@ public class DroneImage { // DJI, autel if tag rtkflag then its already in WGS84 if rtkFlag == true { - print("getAltitude: rtkFlag so already in WGS84") + //print("getAltitude: rtkFlag so already in WGS84") return alt // already in WGS84 } @@ -473,7 +473,7 @@ public class DroneImage { // 10/15/2023 looks like most parrot exif data is NOT in WGS84 if altFromExif == true && !make.lowercased().contains("parrot") && !make.lowercased().contains("autel") { - print("getAltitude: altFromExif, already in WGS84") + //print("getAltitude: altFromExif, already in WGS84") return alt // already in WGS84 } @@ -1347,6 +1347,8 @@ public class DroneImage { var degAzimuth, degTheta: Double var azimuthOffset, thetaOffset: Double + //print("resolveTarget: starting ******") + // azimuth is direction of aircraft's camera 0 is north, increase clockwise // which is reported as Yaw degree // theta is angle of depression (pitch) of aircraft's camera; positive value @@ -1357,19 +1359,19 @@ public class DroneImage { try degAzimuth = getGimbalYawDegree() try degTheta = getGimbalPitchDegree() - print("resolveTarget: got az and theta") + //print("resolveTarget: got az and theta") // corrected or uncorrected versions of getRayAnglesFromImagePixel (azimuthOffset,thetaOffset) = try getRayAnglesFromImagePixelCorrected(x: theImage!.size.width * targetXprop, y: theImage!.size.height * targetYprop) - print("resolveTarget: azOff \(azimuthOffset), thetaOff \(thetaOffset)") + //print("resolveTarget: azOff \(azimuthOffset), thetaOff \(thetaOffset)") degAzimuth += azimuthOffset degTheta += thetaOffset - print("resolveTarget: degAz \(degAzimuth) degTheta \(degTheta)") + //print("resolveTarget: degAz \(degAzimuth) degTheta \(degTheta)") // convert to radians radAzimuth = degAzimuth.radians @@ -1389,7 +1391,7 @@ public class DroneImage { try alt = getAltitudeViaRelative(dem: dem) } - print("resolveTarget: lat: \(lat) lon: \(lon) alt: \(alt)") + print("resolveTarget: starting lat: \(lat) lon: \(lon) alt: \(alt)") } catch { print("resolveTarget: missing metadata at the start") @@ -1451,26 +1453,34 @@ public class DroneImage { // meters of acceptable distance between constructed line and datapoint. somewhat arbitrary var THRESHOLD: Double = post_spacing_meters / 16.0 + print("resolveTarget: setting THRESHOLD \(THRESHOLD)") + var curLat = lat var curLon = lon var curAlt = alt var groundAlt: Double do { try groundAlt = dem.getAltitudeFromLatLong(targetLat: curLat, targetLong: curLon) - //print("resolveTarget: groundAlt is \(groundAlt)") + print("resolveTarget: groundAlt is \(groundAlt)") } catch { throw error } if (curAlt < groundAlt) { - print("curAlt \(curAlt) < groundAlt \(groundAlt) while resolving target") + print("resolveTarget: curAlt \(curAlt) < groundAlt \(groundAlt) while resolving target") throw DroneImageError.BadAltitude //throw ElevationModuleError.RequestedValueOOBError } var altDiff = curAlt - groundAlt + + //print("resolveTarget: entering while loop, altDiff is \(altDiff)") + while altDiff > THRESHOLD { + + //print("resolveTarget: altDiff \(altDiff) > \(THRESHOLD)") + do { try groundAlt = dem.getAltitudeFromLatLong(targetLat: curLat, targetLong: curLon) } @@ -1487,6 +1497,8 @@ public class DroneImage { (curLat,curLon) = DroneImage.inverse_haversine(lat1: curLat, lon1: curLon, d: horizScalar*INCREMENT, radAzimuth: radAzimuth, alt: avgAlt) + //print("resolveTarget: iterate curr lat,lon \(curLat),\(curLon)") + } // while altDiff > threshold // when loop ends, curY, curX, curZ are closeish to target diff --git a/OpenAthenaIOS/DroneImageDJI.swift b/OpenAthenaIOS/DroneImageDJI.swift index 9176c03..4832019 100644 --- a/OpenAthenaIOS/DroneImageDJI.swift +++ b/OpenAthenaIOS/DroneImageDJI.swift @@ -22,7 +22,7 @@ public class DroneImageDJI: DroneImage var alt = 0.0 var gpsInfo: NSDictionary - print("getAltitudeDJI: invoked") + //print("getAltitudeDJI: invoked") let superAlt = try super.getAltitude() @@ -37,10 +37,10 @@ public class DroneImageDJI: DroneImage if metaData!["drone-dji:AbsoluteAltitude"] != nil { alt = (metaData!["drone-dji:AbsoluteAltitude"] as! NSString).doubleValue - print("getAltitudeDJI: drone-dji:AbsoluteAltitude \(alt)") + //print("getAltitudeDJI: drone-dji:AbsoluteAltitude \(alt)") } - print("getAltitudeDJI: alt is \(alt) now going to make corrections") + //print("getAltitudeDJI: alt is \(alt) now going to make corrections") // for DJI drones, look for this flag bug ignore RtkAlt in drone-dji:AltitudeType=RtkAlt var rtkFlag = false diff --git a/OpenAthenaIOS/DroneImageParrot.swift b/OpenAthenaIOS/DroneImageParrot.swift index 2d11696..6428a96 100644 --- a/OpenAthenaIOS/DroneImageParrot.swift +++ b/OpenAthenaIOS/DroneImageParrot.swift @@ -10,6 +10,30 @@ import UIKit public class DroneImageParrot: DroneImage { + // get parrot software version + // return 0.0 if unknown + + public func getVersion() -> String + { + var version: String = "0.0" + + //drone-parrot:SoftwareVersion + if metaData!["drone-parrot:SoftwareVersion"] != nil { + version = metaData!["drone-parrot:SoftwareVersion"] as! String + } + + print("getVersionParrot: \(version)") + + var ret = compareVersions("1.8.0",version) + + print("getVersionParrot 1.8.0 <=> \(version) is \(ret)") + + ret = compareVersions("1.9","1.9.0") + print("getVersionParrot 1.9 <=> 1.9.0 is \(ret)") + + return version + } + // get altitude in meters // use GPS data from digested metadata // GPS altitude or derone specific altitude @@ -147,4 +171,52 @@ public class DroneImageParrot: DroneImage return alt } + // Parrots use NED -- north, east, down -- just like always + // no need to do anything special; code is here just in + // case we want to tweak it. 4/26/2024 + + override public func getGimbalYawDegree() throws -> Double + { + var superYawDegree: Double + + try superYawDegree = super.getGimbalYawDegree() + + print("getGimbalYawDegree parrot is \(superYawDegree)") + + return superYawDegree + } + + // chatgpt derived code! + // compare two version strings of format x.y.z + + func compareVersions(_ version1: String, _ version2: String) -> Int { + let components1 = version1.components(separatedBy: ".") + let components2 = version2.components(separatedBy: ".") + + // Ensure both versions have the same number of components + let maxLength = max(components1.count, components2.count) + let paddedComponents1 = components1 + Array(repeating: "0", count: maxLength - components1.count) + let paddedComponents2 = components2 + Array(repeating: "0", count: maxLength - components2.count) + + // Compare each component numerically + for (component1, component2) in zip(paddedComponents1, paddedComponents2) { + if let num1 = Int(component1), let num2 = Int(component2) { + if num1 < num2 { + return -1 + } else if num1 > num2 { + return 1 + } + } else { + // If components are not numeric, compare them lexicographically + let comparisonResult = component1.compare(component2) + if comparisonResult != .orderedSame { + return comparisonResult == .orderedAscending ? -1 : 1 + } + } + } + + // If all components are equal, versions are equal + return 0 + } + } // DroneImageParrot diff --git a/OpenAthenaIOS/DroneParams.swift b/OpenAthenaIOS/DroneParams.swift index 56ebe34..0e117c3 100644 --- a/OpenAthenaIOS/DroneParams.swift +++ b/OpenAthenaIOS/DroneParams.swift @@ -174,20 +174,33 @@ public class DroneParams // given an image width, find the make/model drone with the // closest match + // re issue #38 sometimes we might match the wrong version of a drone + // that reports same make/model for both thermal and non-thermal + // so, use drone with nearest width by ratio rather than linear distance + func getMatchingDrone(makeModel: String, targetWidth: Double) throws -> DroneCCDInfo? { var theDrones: [DroneCCDInfo] var smallestDifference = Double.infinity var closestDrone: DroneCCDInfo? - var difference: Double + //var difference: Double + var difference_ratio: Double + + print("getMatchingDrone: looking for \(makeModel) \(targetWidth)") try theDrones = getMatchingDrones(makeModel: makeModel) for drone in theDrones { - difference = fabs(drone.widthPixels - targetWidth) - if difference < smallestDifference { + //difference = fabs(drone.widthPixels - targetWidth) + difference_ratio = drone.widthPixels / targetWidth + if difference_ratio < 1.0 { + difference_ratio = 1 / difference_ratio + } + //if difference < smallestDifference { + if difference_ratio < smallestDifference { closestDrone = drone - smallestDifference = difference + //smallestDifference = difference + smallestDifference = difference_ratio } } diff --git a/OpenAthenaIOS/EGM96Geoid.swift b/OpenAthenaIOS/EGM96Geoid.swift index 11b63c9..ca03e24 100644 --- a/OpenAthenaIOS/EGM96Geoid.swift +++ b/OpenAthenaIOS/EGM96Geoid.swift @@ -434,7 +434,7 @@ class EGM96Geoid { // eat blank links or lines with space at beginning -- rdk if line.isEmpty { - print("EGM96Geoid: Skipping parsing empty line") + //print("EGM96Geoid: Skipping parsing empty line") return false } diff --git a/OpenAthenaIOS/LoadCalculateViewController.swift b/OpenAthenaIOS/LoadCalculateViewController.swift index 7611c23..0bd2249 100644 --- a/OpenAthenaIOS/LoadCalculateViewController.swift +++ b/OpenAthenaIOS/LoadCalculateViewController.swift @@ -20,8 +20,9 @@ import ImageIO import MobileCoreServices import UTMConversion import CoreLocation +import mgrs_ios -class LoadCalculateViewController: UIViewController, +class LoadCalculateViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate, UIScrollViewDelegate @@ -451,7 +452,7 @@ class LoadCalculateViewController: UIViewController, { var lat, lon: Double - print("getImageData: starting") + //print("getImageData: starting") if vc.theDroneImage == nil { return @@ -502,7 +503,7 @@ class LoadCalculateViewController: UIViewController, self.htmlString += "Drone altitude: \(error)
" } - print("getImageData: done, going to setTextViewText") + //print("getImageData: done, going to setTextViewText") // display the text finally! setTextViewText(htmlStr: self.htmlString) @@ -704,9 +705,11 @@ class LoadCalculateViewController: UIViewController, if app.settings.outputMode == AthenaSettings.OutputModes.MGRS { let mgrsStr = MGRSGeodetic.WGS84_MGRS1m(Lat: target[1],Lon: target[2],Alt: target[3]) + let mgrsSplitStr = MGRSGeodetic.splitMGRS(mgrs: mgrsStr) + //htmlString += "

MGRS1m: \(mgrsStr)


" let urlStr = "https://www.google.com/maps/search/?api=1&t=k&query=\(mgrsStr)" - htmlString += "

MGRS1m: \(mgrsStr)


" + htmlString += "

MGRS1m: \(mgrsSplitStr)


" if app.settings.unitsMode == .Metric { htmlString += "

Alt: \(altStr)m


" } @@ -715,10 +718,17 @@ class LoadCalculateViewController: UIViewController, } let mgrs10Str = MGRSGeodetic.WGS84_MGRS10m(Lat: target[1], Lon: target[2],Alt: target[3]) - htmlString += "

MGRS10m: \(mgrs10Str)


" + let mgrs10SplitStr = MGRSGeodetic.splitMGRSRegex(mgrs: mgrs10Str) + htmlString += "

MGRS10m: \(mgrs10SplitStr)


" let mgrs100Str = MGRSGeodetic.WGS84_MGRS100m(Lat: target[1], Lon: target[2],Alt: target[3]) - htmlString += "

MGRS100m: \(mgrs100Str)


" + let mgrs100SplitStr = MGRSGeodetic.splitMGRSRegex(mgrs: mgrs100Str) + htmlString += "

MGRS100m: \(mgrs100SplitStr)


" + + //htmlString += "\(mgrsStr)
" + //htmlString += "\(mgrs10Str)
" + //htmlString += "\(mgrs100Str)
" + } // UTM @@ -784,6 +794,18 @@ class LoadCalculateViewController: UIViewController, print("doCalculations: going to get image data") getImageData() + + // display some params from the drone ccd info params that we matched + // to this drone; re issue #38 + + if ccdInfo != nil { + htmlString += "Drone CCD Info for: \(ccdInfo!.makeModel)
" + htmlString += "Drone lens type: \(ccdInfo!.lensType)
" + htmlString += "Drone thermal: \(ccdInfo!.isThermal)
" + htmlString += "Drone image width: \(ccdInfo!.widthPixels)
" + htmlString += "Drone image height: \(ccdInfo!.heightPixels)
" + htmlString += "Drone: \(ccdInfo!.comment)
" + } setTextViewText(htmlStr: htmlString) diff --git a/OpenAthenaIOS/MGRSGeodetic.swift b/OpenAthenaIOS/MGRSGeodetic.swift index 83c5cff..437c17c 100644 --- a/OpenAthenaIOS/MGRSGeodetic.swift +++ b/OpenAthenaIOS/MGRSGeodetic.swift @@ -32,8 +32,66 @@ public class MGRSGeodetic { static func convertToMGRS(Lat: Double, Lon: Double, G: GridType) -> String { var p = GridPoint(Lon,Lat) var m = MGRS.from(p) + + let str = m.coordinate(G) + + // don't split components here because spaces in string + // will break Google Maps URL search + return str + } + + // given an MGRS coordinate in string format, split it into + // subcomponents GZD, square identifier, easting, and northing + // ChatGPT derived code to split apart an MGRS coordinate + // issue #40 + + // this function uses the built-in class components to split the string apart + static func splitMGRS(mgrs: MGRS) -> String + { + return "\(mgrs.zone)\(mgrs.band) \(mgrs.column)\(mgrs.row) \(mgrs.easting) \(mgrs.northing)" + } + + // this function takes a string, converts to MGRS object, then returns split string + // assumes 1m output + static func splitMGRS(mgrs: String) -> String + { + let m = MGRS.parse(mgrs) + let aStr = splitMGRS(mgrs: m) + + print("splitMGRS: \(mgrs) -> \(aStr)") + return aStr + } + + static func splitMGRSRegex(mgrs: String) -> String + { + let pattern = #"^(\d{1,2}[C-Xc-x])([A-HJ-NP-Za-hj-np-z]{2})(\d+)$"# + + guard let regex = try? NSRegularExpression(pattern: pattern, options: []) else { + print("slitMGRS: regex pattern is invalid") + return mgrs + } - return m.coordinate(G) + let range = NSRange(mgrs.startIndex.. 1) } - // current for Nov 30 2023 droneModels release + // current for Apr 28 2024 droneModels release func testLookupDroneParams() { let ccdInfo = try? droneParams!.lookupDrone(makeModel: "djiFC3582") @@ -59,6 +59,47 @@ final class TestDroneParams: XCTestCase { XCTAssert(ccdInfo1!.f == 0) XCTAssert(ccdInfo1!.tangentialT1 == -0.004601610146546272) XCTAssert(ccdInfo1!.tangentialT2 == 0.0026292475166887) + + // verify fix for issue #38 + var ccdInfo2 = try? droneParams!.getMatchingDrone(makeModel: "parrotAnafiUSA", targetWidth: 2304.0) + XCTAssert(ccdInfo2!.isThermal == false) + XCTAssert(ccdInfo2!.radialR1 == -0.249096) + XCTAssert(ccdInfo2!.radialR2 == 0.0199733) + XCTAssert(ccdInfo2!.radialR3 == 0.0116606) + XCTAssert(ccdInfo2!.tangentialT1 == 0.0001663350) + XCTAssert(ccdInfo2!.tangentialT2 == 0.000881907) + + ccdInfo2 = try? droneParams!.getMatchingDrone(makeModel: "DJIfc2204", targetWidth: 4000.0) + XCTAssert(ccdInfo2!.isThermal == false) + XCTAssert(ccdInfo2!.radialR1 == 0.0) + XCTAssert(ccdInfo2!.radialR2 == 0.0) + XCTAssert(ccdInfo2!.radialR3 == 0.0) + XCTAssert(ccdInfo2!.tangentialT1 == 0.0) + XCTAssert(ccdInfo2!.tangentialT2 == 0.0) + + ccdInfo2 = try? droneParams!.getMatchingDrone(makeModel: "autel roboticsxl709", targetWidth: 640.0) + XCTAssert(ccdInfo2!.isThermal == true) + XCTAssert(ccdInfo2!.radialR1 == 0.0) + XCTAssert(ccdInfo2!.radialR2 == 0.0) + XCTAssert(ccdInfo2!.radialR3 == 0.0) + XCTAssert(ccdInfo2!.tangentialT1 == 0.0) + XCTAssert(ccdInfo2!.tangentialT2 == 0.0) + + ccdInfo2 = try? droneParams!.getMatchingDrone(makeModel: "parrotBEBOP 2", targetWidth: 4000) + XCTAssert(ccdInfo2!.isThermal == false) + XCTAssert(ccdInfo2!.lensType == "fisheye") + XCTAssert(ccdInfo2!.c == 2203.93) + XCTAssert(ccdInfo2!.d == 0.0) + XCTAssert(ccdInfo2!.e == 0.0) + XCTAssert(ccdInfo2!.f == 2203.93) + + ccdInfo2 = try? droneParams!.getMatchingDrone(makeModel: "parrotANAFITHERMAL", targetWidth: 320.0) + XCTAssert(ccdInfo2!.isThermal == true) + XCTAssert(ccdInfo2!.radialR1 == 0.0) + XCTAssert(ccdInfo2!.radialR2 == 0.0) + XCTAssert(ccdInfo2!.radialR3 == 0.0) + XCTAssert(ccdInfo2!.tangentialT1 == 0.0) + XCTAssert(ccdInfo2!.tangentialT2 == 0.0) } func testMatchingDroneParams()