ios – The best way to decide the latitude and longitude of a sighting at sea stage from a raised level on land

0
16
ios – The best way to decide the latitude and longitude of a sighting at sea stage from a raised level on land


First off, I’m new to Swift, however have been a software program programs engineer for the final 40 years. I’m fluent in C, C++, Java and Python.

I’ve applied a model of the next resolution in Python utilizing inverse_haversine(), however I want to make it extra correct and hopefully make the most of among the CLLocation framework and any built-in or out there libraries that calculate geodetics.

So, that being mentioned, right here is the issue I’m attempting to unravel utilizing Swift:

Given:

  • Observer’s Latitude (𝝏1)

  • Observer’s Longitude (𝝄1)

  • Observer’s Peak above sea stage (h)

  • Radius of the Earth (R)

And:

  • Bearing from Observer (O) to Goal (T)

  • The angle shaped by the straight line between Observer (O) and Goal (T) relative to the tangent of the earth’s circumference on the location of the Observer (O)

Calculate:

  • The Latitude (𝝏2) and Longitude(𝝄2) of Goal

enter picture description right here Illustration of the issue

As talked about above, I’ve written a program in Python that performs most of those calculations, nonetheless as an alternative of utilizing the angle shaped by the observer’s downward viewing angle and the tangent to the sphere on the location of the observer, I take advantage of the reticle of binoculars measuring the variety of mils between the horizon and the goal object.

I want to not depend on the reticle of the binoculars, however fairly use the gyro within the iPhone to find out the above angle, and utilizing that within the calculations as an alternative.

For reference, right here is the Python code I’ve written:

<

mport sys
from math import sin, cos, asin, acos, atan2, sqrt, pi
from haversine import haversine, inverse_haversine, inverse_haversine_vector, Unit, Path
from numpy import arange

#===================================================================================#
#                        UNIT CONVERSIONS FACTOR CONSTANTS                          #
#===================================================================================#
feetToMiles                 = 0.000189394
feetToMeters                = 0.30480030800000379454
feetToKilometers            = 0.0003048000097536
feetToNauticalMiles         = 0.000164579

milesToFeet                 = 5280
milesToMeters               = 1609.34
milesToKilometers           = 1.60934
milesToNauticalMiles        = 0.868976

metersToFeet                = 3.28084
metersToMiles               = 0.000621371
metersToKilometers          = 0.001
metersToNauticalMiles       = 0.000539957349081371537

kilometersToFeet            = 3280.84
kilometersToMiles           = 0.621371
kilometersToMeters          = 1000
kilometersToNauticalMiles   = 0.53995734908137149599

nauticalMilesToFeet         = 6076.11568
nauticalMilesToMiles        = 1.15078
nauticalMilesToMeters       = 1852.000059264
nauticalMilesToKilometers   = 1.852

degreesToRadians            = pi / 180
radiansToDegrees            = 180 / pi

englishHorizonCoefficient   = 1.22459
metricHorizonCoefficient    = 3.56972
#===================================================================================#
#                    CLASSES FOR LOCATION, BEARING AND DISTANCE                     #
#===================================================================================#
class Bearing:
        def __init__(self, bearing: float, unit:Unit = None):
             self.bearing = bearing
             self.unit    = unit

        def __str__(self):
             return f"Bearing: {spherical(self.bearing, 4):3} {self.unit.worth}"
        
        def levels(self):
            if self.unit == Unit.DEGREES:
                return self.bearing
            else:
                return self.bearing * radiansToDegrees
        def radians(self):
            if (self.unit == Unit.RADIANS):
                return self.bearing
            else:
                return self.bearing * degreesToRadians

        def convertTo(self, unit: Unit):
            if unit == self.unit:
                return self
            match unit:
                case Unit.DEGREES:
                    return Bearing(self.levels(), unit)
                case Unit.RADIANS:
                    return Bearing(self.radians(), unit)
                case _:
                    elevate Exception(f"Invalid Bearing unit specified: {unit}")
                 

            
            
class Distance:
        def __init__(self, distance: float, unit: Unit = Unit.MILES):
            self.distance = distance
            self.unit     = unit

        def __str__(self):
             return f"Distance: {spherical(self.distance, 4):8.4} {self.unit.worth}"

        def toes(self):
            match self.unit:
                case Unit.FEET:
                    return self.distance
                case Unit.MILES:
                    return self.distance * milesToFeet
                case Unit.METERS:
                    return self.distance * metersToFeet
                case Unit.KILOMETERS:
                    return self.distance * kilometersToFeet
                case Unit.NAUTICAL_MILES:
                    return self.distance * nauticalMilesToFeet
                case _:
                    elevate Exception(f"Invalid Distance unit specified: {unit}")

        def miles(self):
            match self.unit:
                case Unit.FEET:
                    return self.distance * feetToMiles
                case Unit.MILES:
                    return self.distance
                case Unit.METERS:
                    return self.distance * metersToMiles
                case Unit.KILOMETERS:
                    return self.distance * kilometersToMiles
                case Unit.NAUTICAL_MILES:
                    return self.distance * nauticalMilesToMiles
                case _:
                    elevate Exception(f"Invalid Distance unit specified: {unit}")

        def meters(self):
            match self.unit:
                case Unit.FEET:
                    return self.distance * feetToMeters
                case Unit.MILES:
                    return self.distance * milesToMeters
                case Unit.METERS:
                    return self.distance
                case Unit.KILOMETERS:
                    return self.distance * kilometersToMeters
                case Unit.NAUTICAL_MILES:
                    return self.distance * nauticalMilesToMeters
                case _:
                    elevate Exception(f"Invalid Distance unit specified: {unit}")

        def kilometers(self):
            match self.unit:
                case Unit.FEET:
                    return self.distance * feetToKilometers
                case Unit.MILES:
                    return self.distance * milesToKilometers
                case Unit.METERS:
                    return self.distance * metersToKilometers
                case Unit.KILOMETERS:
                    return self.distance
                case Unit.NAUTICAL_MILES:
                    return self.distance * nauticalMilesToKilometers
                case _:
                    elevate Exception(f"Invalid Distance unit specified: {unit}")

        def nauticalMiles(self):
            match self.unit:
                case Unit.FEET:
                    return self.distance * feetToNauticalMiles
                case Unit.MILES:
                    return self.distance * milesToNauticalMiles
                case Unit.METERS:
                    return self.distance * metersToNauticalMiles
                case Unit.KILOMETERS:
                    return self.distance * kilometersToNauticalMiles
                case Unit.NAUTICAL_MILES:
                    return self.distance
                case _:
                    elevate Exception(f"Invalid Distance unit specified: {unit}")

        def convertTo(self, unit: Unit):
            if unit == self.unit:
                return self
            match unit:
                case Unit.FEET:
                    return Distance(self.toes(), unit)
                case Unit.MILES:
                    return Distance(self.miles(), unit)
                case Unit.METERS:
                    return Distance(self.meters(), unit)
                case Unit.KILOMETERS:
                    return Distance(self.kilometers(), unit)
                case Unit.NAUTICAL_MILES:
                    return Distance(self.nauticalMiles(), unit)
                case _:
                    elevate Exception(f"Invalid Distance unit specified: {unit}")

class Peak:
        def __init__(self, peak: float, unit = Unit.MILES):
            self.peak = peak
            self.unit   = unit

        def __str__(self):
             return f"Peak: {spherical(self.peak, 1)} {self.unit.worth}"

        def toes(self):
            match self.unit:
                case Unit.FEET:
                    return self.peak
                case Unit.MILES:
                    return self.peak * milesToFeet
                case Unit.METERS:
                    return self.peak * metersToFeet
                case Unit.KILOMETERS:
                    return self.peak * kilometersToFeet
                case Unit.NAUTICAL_MILES:
                    return self.peak * nauticalMilesToFeet
                case _:
                    elevate Exception(f"Invalid Peak unit specified: {unit}")
                
        def miles(self):
            match self.unit:
                case Unit.FEET:
                    return self.peak * feetToMiles
                case Unit.MILES:
                    return self.peak
                case Unit.METERS:
                    return self.peak * metersToMiles
                case Unit.KILOMETERS:
                    return self.peak * kilometersToMiles
                case Unit.NAUTICAL_MILES:
                    return self.peak * nauticalMilesToMiles
                case _:
                    elevate Exception(f"Invalid Peak unit specified: {unit}")


        def meters(self):
            match self.unit:
                case Unit.FEET:
                    return self.peak * feetToMeters
                case Unit.MILES:
                    return self.peak * milesToMeters
                case Unit.METERS:
                    return self.peak
                case Unit.KILOMETERS:
                    return self.peak * kilometersToMeters
                case Unit.NAUTICAL_MILES:
                    return self.peak * nauticalMilesToMeters
                case _:
                    elevate Exception(f"Invalid Peak unit specified: {unit}")

        def kilometers(self):
            match self.unit:
                case Unit.FEET:
                    return self.peak * feetToKilometers
                case Unit.MILES:
                    return self.peak * milesToKilometers
                case Unit.METERS:
                    return self.peak * metersToKilometers
                case Unit.KILOMETERS:
                    return self.peak
                case Unit.NAUTICAL_MILES:
                    return self.peak * nauticalMilesToKilometers
                case _:
                    elevate Exception(f"Invalid Peak unit specified: {unit}")

        def nauticalMiles(self):
            match self.unit:
                case Unit.FEET:
                    return self.peak * feetToNauticalMiles
                case Unit.MILES:
                    return self.peak * milesToNauticalMiles
                case Unit.METERS:
                    return self.peak * metersToNauticalMiles
                case Unit.KILOMETERS:
                    return self.peak * kilometersToNauticalMiles
                case Unit.NAUTICAL_MILES:
                    return self.peak
                case _:
                    elevate Exception(f"Invalid Peak unit specified: {unit}")

        def convertTo(self, unit: Unit):
            if unit == self.unit:
                return self
            match unit:
                case Unit.FEET:
                    return Peak(self.toes(), unit)
                case Unit.MILES:
                    return Peak(self.miles(), unit)
                case Unit.METERS:
                    return Peak(self.meters(), unit)
                case Unit.KILOMETERS:
                    return Peak(self.kilometers(), unit)
                case Unit.NAUTICAL_MILES:
                    return Peak(self.nauticalMiles(), unit)
                case _:
                    elevate Exception(f"Invalid Peak unit specified: {unit}")

class Coordinate:
        def __init__(self, coord: float, unit: Unit = Unit.RADIANS):
            self.coord  = coord
            self.unit   = unit

        def __str__(self):
            return f"Coordinate: {spherical(self.coord, 4)} {self.unit.worth:4.4}"
        
        def radians(self):
            match self.unit:
                case Unit.DEGREES:
                    return self.coord * degreesToRadians
                
                case Unit.RADIANS:
                    return self.coord
            
        def levels(self):
            match self.unit:
                case Unit.DEGREES:
                    return self.coord
                
                case Unit.RADIANS:
                    return self.coord * radiansToDegrees

        def convertTo(self, unit: Unit):
            if unit == self.unit:
                return self
            match unit:
                case Unit.DEGREES:
                    return Coordinate(self.levels(), unit)
                case Unit.RADIANS:
                    return Coordinate(self.radians(), unit)
                case _:
                    elevate Exception(f"Invalid Coordinate unit specified: {unit}")
                    
            
class Location:
        def __init__(self, lat: float, lon: float, h: Peak, cUnit: Unit = Unit.DEGREES):
            self.lat   = Coordinate(lat % 360, cUnit)
            self.lon   = Coordinate(lon % 360, cUnit)
            self.h     = h

        def __str__(self):
            return f"Location: {self.lat}, {self.lon}, {self.h}"
        
        def latRad(self):
            return self.lat.radians()
        
        def latDeg(self):
            return self.lat.levels()
        
        def lonRad(self):
            return self.lon.radians()
        
        def lonDeg(self):
            return self.lon.levels()
        
        def latLonRad(self):
            return self.latRad(), self.lonRad()
        
        def latLonDeg(self):
            return self.latDeg(), self.lonDeg()
        
        def peak(self):
            return self.h

        def convertTo(self, unit: Unit):
            match unit:
                case Unit.DEGREES:
                    return Location(self.lat.levels(),self.lon.levels(), self.h, unit)
                case Unit.RADIANS:
                    return Location(self.lat.radians(),self.lon.radians(), self.h, unit)
                case _:
                    elevate Exception(f"Invalid Location unit specified: {unit}")


#===================================================================================#
#                                   CONSTANTS                                       #
#===================================================================================#
R                           = Distance(3960, Unit.MILES)                            # Radius of the earth
census                      = Location(33.74475, -118.4107, Peak(143.5, Unit.FEET))
#===================================================================================#


#===================================================================================#
#                                   METHODS                                         #
#===================================================================================#
def reticleToMils(reticle):
    return reticle * 5

def reticleToDistance(observer, reticle):
        height_meters = observer.peak().meters()
        height_miles  = observer.peak().miles()
        mils   = reticleToMils(reticle)

        if mils < 0:
            elevate(Exception(f"Mils can't be destructive: {mils}"))

        if mils > 0:
            print(f"XXXXX: {Distance(spherical(height_meters * 1000 / mils, 1), Unit.METERS)}, {Distance(spherical(height_miles * 1000 / mils, 1), Unit.MILES)}")
            return Distance(spherical(height_meters * 1000 / mils, 1), Unit.METERS)
        else:
            print(f"YYYYY: {Distance(metricHorizonCoefficient * sqrt(height_meters), Unit.METERS)}, {Distance(englishHorizonCoefficient * sqrt(height_meters), Unit.MILES)}")
            return Distance(metricHorizonCoefficient * sqrt(height_meters), Unit.METERS)


def getTargetLocation(observer: Location, reticle, b: Bearing):
    d = reticleToDistance(observer, reticle)
    print(f"{spherical(d.miles(), 2)} miles")
    goal = inverse_haversine(observer.latLonRad(), d.meters(), b.radians())
    return Location(goal[0], goal[1], Peak(0), Unit.RADIANS)
    
#===================================================================================#
#                                      DEBUG CODE                                   #
#===================================================================================#
#===================================================================================#
#                                    OUTPUT FILES                                   #
#===================================================================================#
listing                   = '/Customers/mbi/Paperwork/Programming/Whale Locator/'
filenameBase="Take a look at Run - "
def dumpLatLongTable(observer: Location, b: Bearing):
    if listing:
        f = open(f"{listing}{filenameBase}{b.levels()}.csv", "w")
    else:
        f = open(f"{filenameBase}{b.levels()}.csv", "w")

    f.write("Lattitude, Longituden")
    for reticle in arange(0, 20.1, 0.1):
        d = reticleToDistance(observer, reticle)
        goal = inverse_haversine(census.latLonRad(), d.meters(), b.radians()) 
        l = Location(goal[0], goal[1], 0, Unit.RADIANS)
        f.write(f"{l.latDeg()}, {l.lonDeg()}n")
        print(f"{spherical(reticle, 1):4},   {d.convertTo(Unit.MILES)},   {b},   {l.convertTo(Unit.DEGREES)}")
    print()

def dumpLatLongTables(observer: Location):
    for bearing in arange(0, 360, 15):
        dumpLatLongTable(observer, Bearing(bearing, Unit.METERS))

def dumpReticleDistanceTable(observer):
    for reticle in (0, 0.1, 0.2, 0.4, 0.6, 0.8, 1, 1.2, 1.4, 1.6, 1.8, 2, 2.4, 2.6, 3, 3.4, 4, 4.6, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20):
        d = reticleToDistance(observer, reticle)
        print(f"{spherical(reticle, 1):4}, {float(spherical(reticleToMils(reticle), 1)):10.6}, {d.miles():10.8}")

#
# Primary Executable
#
if __name__ == "__main__":
    dumpReticleDistanceTable(census)
    dumpLatLongTable(census, Bearing(270, Unit.METERS))

>

LEAVE A REPLY

Please enter your comment!
Please enter your name here