'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