Search code examples
ioscompass-geolocationheading

Convert user entered true azimuth to magnetic azimuth


I was wondering if there is a way to use CLHeading or CLLocationDirection to convert between true and magnetic azimuth for a USER entered value.

In a normal scenario with plenty of examples on SO, we can use didUpdateHeading:(CLocation) newHeading with newHeading.magneticHeading or trueHeading to get those values when we want the device bearing/heading. Both trueHeading and magneticHeading are read only properties. However what I need is to provide a user entered true azimuth to CLocation or something else and return the magnetic value corresponding to the input.

One way to do that is to get the updated magnetic field model of the earth from NOAA (or other sources) and write code to do this calculation based on the user location. However this has many drawbacks and requires constant updates to the model array in the app code since the model changes all the time. Using any built-in model would be great...

UPDATE (More Information to understand declination):

I tested the declination for the DC area and it correctly returned -10.7 degrees with the simple function in the answer by @rokjarc shown below. For reference on the declination in the US, please see the following map (Image Source - NOAA): enter image description here

Later I will be deploying this on a friend's device in Houston, TX area where the declination is 2 degrees to further confirm.

For those who wonder, when you use Apple's MapKit, the map is always oriented so that true north is situated at the top of the map -- source -- (http://developer.apple.com/library/ios/documentation/MapKit/Reference/MKMapView_Class/) With the above information, you should always use true heading and NOT magnetic if displaying a POI with a bearing or azimuth relative to a location like what I was trying to achieve...

NOTE: Declination changes all the time (you may have heard of pole shift). If you test this in 2016 for example, DC may have a declination of -5 or -20 etc instead of the current -10. When using this method to calculate the declination you do not have to worry about changes in declination over time since the compass (I believe) will sense the magnetic field of the earth and return the correct heading.magneticHeading


Solution

  • From the last paragraph of your question it seems that you are interested in conversion of arbitrary magnetic heading to true heading on current location of the user.

    In this case you can assign an ivar (or property) called variation and update regulary in didUpdateHeading:.

    //instantiated as ivars or properties:
    
    NSDate *variationTimestamp;
    double variation; //or CLLocationDirection
    CLLocationManager *locationManager;   
    
    -(void)locationManager:(CLLocationManager *)manager didUpdateHeading:(CLHeading *)newHeading
    {
        CLLocation *location = locationManager.location;
    
        //check if location is valid - it has to be for variation data to be valid too
    
        if ((newHeading.headingAccuracy > 0) && (location.horizontalAccuracy > 0))
        {
            variation = newHeading.trueHeading - newHeading.magneticHeading;
            variationTimestamp = newHeading.timestamp;
        }
    }
    

    Since magnetic variation if an f(location) - it is the same for all magnetic headings on the same location - you can use this variation for calculations with user input.

    The obvious drawback is that you have to have fresh location/heading data. Note: besides enabling heading updates you have to also enable location updates.