Question

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

Was it helpful?

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.

OTHER TIPS

Most likely as you get realtime location updates, the true heading is being derived from the GPS data as you move from one point to another while the magnetic heading is coming from the compass. This of course only works in realtime on the user's current location.

The Core Location framework has no API to give you the magnetic variation for an arbitrary location. The solution described in your last paragraph is your only viable solution.

Keep in mind that there can't be a built-in model for the reason you already mention - it changes.

If you read the license notes of Apples iphone, you get a list of all open source they use in iphone. (Menu Common->Info->Copyright->License) One of that is the geomagnetic earth modell, developped by NOAA. You can download the source and implement that too: Geomag: https://www.ngdc.noaa.gov/WMM/

This would have the advantage that you can calculate the magnetic variation for any coordinate on world, not only the current one. For most places the relative change of the azimuth per year is known and specified, like in better topgraphic maps. You can then extrapolate to current time when knowing the date of the geo mag modell.

However in most cases it is sufficient to know the magnetic variation for current location. In that case reading it from CLLocation is possible (as described by User rokjarc)

I needed a solution to find magnetic declination at a specified location in Swift, based on the World Magnetic Model.

Here is my implementation: https://github.com/kanchudeep/Geomagnetism-Swift

It's a simple single class which can be dropped into any project and used.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top