I wish to create the compass / arrow exactly like the one we see in AroundMe Mobile App that point exactly to a pin to the map accordingly with my mobile position and update the arrow when I move the phone.
I'm getting crazy to understand exactly how to do that and I can not find any guide or tutorial that explain a bit it.
What I found online is a bearing function and I created a directive around it:
app.directive('arrow', function () { function bearing(lat1, lng1, lat2, lng2) { var dLon = (lng2 - lng1); var y = Math.sin(dLon) * Math.cos(lat2); var x = Math.cos(lat1) * Math.sin(lat2) - Math.sin(lat1) * Math.cos(lat2) * Math.cos(dLon); var rad = Math.atan2(y, x); var brng = toDeg(rad); return (brng + 360) % 360; } function toRad(deg) { return deg * Math.PI / 180; } function toDeg(rad) { return rad * 180 / Math.PI; } return { restrict: 'E', link: function (scope, element, attrs) { var arrowAngle = bearing(scope.user.position.lat, scope.user.position.lng, attrs.lat, attrs.lng); element.parent().css('transform', 'rotate(' + arrowAngle + 'deg)'); } }; });
It seems to update the arrow in a direction but unfortunately it is not the right direction because it is not calculated using also the mobile magneticHeading
position.
So I added the ngCordova
plugin for Device Orientation to get the magneticHeading
and now I don't know exactly how to use it and where in the bearing
function.
$cordovaDeviceOrientation.getCurrentHeading().then(function(result) { var magneticHeading = result.magneticHeading; var arrowAngle = bearing(scope.user.position.lat, scope.user.position.lng, attrs.lat, attrs.lng, magneticHeading); element.parent().css('transform', 'rotate(' + arrowAngle + 'deg)'); });
I tried to add it in the return statement:
return (brng - heading) % 360;
or:
return (heading - ((brng + 360) % 360));
Implementing this code with a watcher I see the arrow moving but not in the exact position... For example from my position and the pin the arrow should point to N and it is pointing to E.
Always looking online I can not find any tutorial / question to find the bearing between a lat/lng point
and a magnetingHeading
.
Maybe I'm close to the solution but I can not go ahead alone.
I also tried to search for a mathematical formulas but even there is a huge pain to understand and implement it.
I hope you can help.
It's hard to give a plain answer to this question because a lot depends on the actual graphical representation. For instance, in what direction do you point where rotate(0deg)
.
I can explain the formula you've found, which might help you to clear the issue yourself. The hard part is the following:
var dLon = (lng2 - lng1); var y = Math.sin(dLon) * Math.cos(lat2); var x = Math.cos(lat1) * Math.sin(lat2) - Math.sin(lat1) * Math.cos(lat2) * Math.cos(dLon); var rad = Math.atan2(y, x);
What you see here is Haversines formula (https://en.wikipedia.org/wiki/Haversine_formula). Normally one could suffice with two sets of coordinates and calculate the angle between them. When working with latitude and longitude this will not work because the earth is not a flat surface. The first three lines are Haversine and the result (x
and y
) are coordinates on the unit circle
(https://en.wikipedia.org/wiki/Unit_circle)
The next step is to calculate the angle from the point on this unit circle to the center. We can use the arctangant
for this. Javascript has a helper function atan2
which simplifies this process. The result is simple the angle of your point towards the center of the circle. In other words, your position towards your point of interest. The result is in radians and needs to be converted to degrees. (https://en.wikipedia.org/wiki/Atan2)
A simplified version with a flat coordinate system would look like this:
var deltaX = poi.x - you.x; var deltaY = you.y - poi.y; var rotation = toDeg(Math.atan2(deltaX, deltaY)); bearingElement.css('transform', 'rotate(' + rotation + 'deg)');
Where poi
is the Point of Interest and you
is your position.
To compensate for your own rotation, you need to substract your own rotation. In the above sample the result would become:
var deltaX = poi.x - you.x; var deltaY = you.y - poi.y; var rotation = toDeg(Math.atan2(deltaX, deltaY)); rotation -= you.rotation; bearingElement.css('transform', 'rotate(' + rotation + 'deg)');
I've made a Plunckr in which you can see a simple flat coordinate system. You can move and rotate you
and the point of interest
. The arrow inside 'you' will always point towards the poi
, even if you rotate 'you'. This is because we compensate for our own rotation.
https://plnkr.co/edit/OJBGWsdcWp3nAkPk4lpC?p=preview
Note in the Plunckr that the 'zero'-position is always to the north. Check your app to see your 'zero'-position. Adjust it accordingly and your script will work.
Hope this helps :-)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With