'Normalised compass bearing from start to end point as lat, lng pairs
From the net:
Since atan2 returns values in the range -π ... +π (that is, -180° ... +180°), to normalise the result to a compass bearing (in the range 0° ... 360°, with -ve values transformed into the range 180° ... 360°), convert to degrees and then use (θ+360) % 360, where % is modulo.
I'm using VB6 and my code is like this
brng = (θ + 360) Mod 360
where θ = -68
I'm getting the answer 292°,but expected answer is 248°.
am i committing a mistake..? or am I missing something.? Please help me.
Update:
I'll further explain my question,
Dim b As Double
Dim x As Double
Dim y As Double
Dim Dlat As Double
Dim DLon As Double
Private Const pi As Double = 3.14159265358979
Public Function Atn2(ByVal y As Double, ByVal x As Double) As Double
On Error GoTo DivideError
Atn2 = Atn(y / x)
If (x < 0) Then
If (y < 0) Then Atn2 = Atn2 - vbPI Else Atn2 = Atn2 + vbPI
End If
Exit Function
DivideError:
If Abs(y) > Abs(x) Then 'Must be an overflow
If y > 0 Then Atn2 = vbPI / 2 Else Atn2 = -vbPI / 2
Else
Atn2 = 0 'Must be an underflow
End If
Resume Next
End Function
Public Sub AFAMP()
lat1 = Val(Text1.Text) * pi / 180 'conveting to radians
lat2 = Val(Text2.Text) * pi / 180
Long1 = Val(Text3.Text) * pi / 180
Long2 = Val(Text4.Text) * pi / 180
Dlat = (lat1 - lat2)
DLon = (Long1 - Long2)
y = Math.Sin(DLon) * Math.Cos(lat2)
x = Math.Cos(lat1) * Math.Sin(lat2) - Math.Sin(lat1) * Math.Cos(lat2) * Math.Cos(DLon)
b = Atn2(y, x) * (180 / pi)
Text5.Text = (b +360) mod 360
End Sub
coordinates are as below in decimal degrees
lat1 = Val(7.337361) * pi / 180
lat2 = Val(7.000667) * pi / 180
Long1 = Val(81.626198) * pi / 180
Long2 = Val(80.773737) * pi / 180
I have checked with an online coordinate calculator, the answer is 248. In my case it is 292 degrees. Can anyone please help me? I'm stuck.
Solution 1:[1]
As Deanna said in a comment, 292 is the correct answer. -68 + 360 = 292
Solution 2:[2]
This code is JavaScript because I can't remember VB syntax.
Convert to degrees. Don't repeat this code, make it a method on Number. Or a first class function with two parameters, whatever your target language supports.
Number.prototype.toDegrees = function (unit) {
const u = (unit || "d").toLowerCase().charAt(0);
switch (u) {
case 'r': //radians
return this * 180 / Math.PI;
case 'g': //grads
return this * 200 / Math.PI;
case 'd': //degrees
return this;
default: //radians
return this * 180 / Math.PI;
}
};
Normalise the range.
Number.prototype.toNormalisedDegrees = function () {
return (this - 360 * (Math.trunc(this / 360) - 1)) % 360;
}
Rotate 90 degrees and reverse the direction.
Number.prototype.toCompassBearing = function (unit) {
const degrees = unit && unit.toLowerCase().charAt(0) === "r" ? this.toDegrees() : this;
return (90 - degrees).toNormalisedDegrees();
}
Test with some known values
Number.prototype.toDegrees = function (unit) {
const u = (unit || "d").toLowerCase().charAt(0);
switch (u) {
case 'r':
return this * 180 / Math.PI;
case 'g':
return this * 200 / Math.PI;
default:
return this;
}
};
Number.prototype.toNormalisedDegrees = function () {
return (this - 360 * (Math.trunc(this / 360) - 1)) % 360;
}
Number.prototype.toCompassBearing = function (unit) {
return (90 - this.toDegrees(unit)).toNormalisedDegrees();
}
console.log(`0 cartesian (E) => ${(0).toCompassBearing("d")} compass bearing`);
console.log(`90 cartesian (N) => ${(90).toCompassBearing("d")} compass bearing`);
console.log(`90-22.5 cartesian (NNE) => ${(90-22.5).toCompassBearing("d")} compass bearing`);
console.log(`180 cartesian (W) => ${(180).toCompassBearing("d")} compass bearing`);
console.log(`-68 cartesian => ${(-68).toCompassBearing("d")} compass bearing`);
console.log(`-68 compass bearing => ${(-68).toNormalisedDegrees()} normalised degrees`);
console.log(`270 cartesian (S) => ${(270).toCompassBearing("d")} compass bearing`);
console.log(`0 cartesian (E) => ${(0).toCompassBearing("r")} compass bearing`);
console.log(`PI/2 cartesian (N) => ${(0.5 * Math.PI).toCompassBearing("r")} compass bearing`);
console.log(`PI cartesian (W) => ${Math.PI.toCompassBearing("r")} compass bearing`);
console.log(`1.5 PI cartesian (S) => ${(1.5 * Math.PI).toCompassBearing("r")} compass bearing`);
console.log(`2 PI cartesian (E) => ${(2 * Math.PI).toCompassBearing("r")} compass bearing`);
console.log(`20 PI cartesian (E) => ${(20 * Math.PI).toCompassBearing("r")} compass bearing`);
console.log(`-2 PI cartesian (E) => ${(-2 * Math.PI).toCompassBearing("r")} compass bearing`);
console.log(`-2.5 PI cartesian (S) => ${(-2.5 * Math.PI).toCompassBearing("r")} compass bearing`);
console.log(`-3 PI cartesian (W) => ${(-3 * Math.PI).toCompassBearing("r")} compass bearing`);
console.log(`-3.5 PI cartesian (N) => ${(-3.5 * Math.PI).toCompassBearing("r")} compass bearing`);
If 292 is right, what's this 248 business?
You asked about converting a compass heading into normalised degrees, but the sample code you provide and the result you think is correct both suggest that what you actually want to do is determine the heading defined by start and end points expressed as Lat,Lng pairs, and express this in normalised degrees.
There's an article here suggesting a method and your code looks like a botched attempt to implement this.
This is what it looks like if you get it right in JavaScript.
Number.prototype.toDegrees = function(unit) {
const u = (unit || "d").toLowerCase().charAt(0);
switch (u) {
case 'r':
return this * 180 / Math.PI;
case 'g':
return this * 200 / Math.PI;
default:
return this;
}
};
Number.prototype.toRadians = function() {
return this * Math.PI / 180;
}
Number.prototype.toNormalisedDegrees = function() {
return (this - 360 * (Math.trunc(this / 360) - 1)) % 360;
}
function bearing(lat1, lng1, lat2, lng2) {
const X = Math.cos(lat2) * Math.sin(lng2 - lng1);
const Y = Math.cos(lat1) * Math.sin(lat2) - Math.sin(lat1) * Math.cos(lat2) * Math.cos(lng2 - lng1);
return Math.atan2(X, Y);
}
const lat1 = (7.337361).toRadians();
const lng1 = (81.626198).toRadians();
const lat2 = (7.000667).toRadians();
const lng2 = (80.773737).toRadians();
const bearingRadians = bearing(lat1, lng1, lat2, lng2);
const bearingDegrees = bearingRadians.toDegrees("radians");
console.log(`bearingRadians = ${bearingRadians}`);
console.log(`bearingDegrees = ${bearingDegrees}`);
console.log(`normalisedDegrees = ${bearingDegrees.toNormalisedDegrees()}`);
There are a couple of things to note.
- Latitude and Longitude are in the compass frame of reference and do not require conversion from the cartesian frame of reference.
- This code is written so that the result is in the compass frame of reference.
- The result is in radians and then converted to degrees.
- The degrees are then normalised to the 0-360 range.
- Using your inputs I get your expected output.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | MarkJ |
| Solution 2 |
