Feedback

C# - Entfernung, Kurswinkelberechnung geographische Koordinaten

Veröffentlicht von am 11/20/2009
(2 Bewertungen)
Mit diesen 2 Klassen können geographische Koordinaten in Längen- und Breitengraden angelegt werden, desweiteren können Entfernungen zwischen 2 Punkten in KM oder NM errechnet werden sowie der Rechtsweisende Kurswinkel errechnet werden.
/// <summary>
/// Eine Geokoordinate aus Längen- und Breitengrad
/// </summary>
public class GeoCoordinate
{
    private GeoPoint m_Latitude;
    private GeoPoint m_Longitude;

    public const double EARTHRADIUSKM = 6378.137;
    public const double NMFACTOR = 1.852216;
    public const double EARTHRADIUSNM = EARTHRADIUSKM / NMFACTOR;
    public const double EARTHPLATTUNG = 1.0 / 298.257223563;

    #region Constructor
    /// <summary>
    /// Erstellt eine neue Geokoordinate mit den angegebenen Koordinaten
    /// </summary>
    /// <param name="latitude">Breitengrad in Dezimalgrad</param>
    /// <param name="longitude">Längengrad in Dezimalgrad</param>
    public GeoCoordinate( double latitude, double longitude )
    {
        m_Latitude = new GeoPoint( latitude < 0 ? "S" : "N", latitude );
        m_Longitude = new GeoPoint( longitude < 0 ? "E" : "W", longitude );
    }

    /// <summary>
    /// Erstellt eine neue Geokoordinate aus den angegebenen GeoPoints
    /// </summary>
    /// <param name="latitude">Breitengrad</param>
    /// <param name="longitude">Längengrad</param>
    public GeoCoordinate( GeoPoint latitude, GeoPoint longitude )
    {
        if( latitude.Indicator != "N" && latitude.Indicator != "S" )
            throw new ArgumentException( "Latitude must be N or S!" );

        if( longitude.Indicator != "W" && longitude.Indicator != "E" )
            throw new ArgumentException( "Longitude must be W or E!" );

        m_Latitude = latitude;
        m_Longitude = longitude;
    }
    #endregion

    #region Static Methods
    /// <summary>
    /// Berechnet die Länge des Orthodroms zwischen zwei Punkten
    /// </summary>
    /// <remarks>Versucht eine Berechnung nach WGS84</remarks>
    /// <param name="c1">Erster Punkt</param>
    /// <param name="c2">Zweiter Punkt</param>
    /// <returns>Entfernung in Kilometern</returns>
    public static double DistanceKM( GeoCoordinate c1, GeoCoordinate c2 )
    {
        double dist = 0.0;

        try
        {
            dist = CalcDistanceWGS84( c1, c2, EARTHRADIUSKM );
        }
        catch( Exception ex )
        {
            dist = CalcDistance( c1, c2 ) * EARTHRADIUSKM;
        }

        return dist;
    }

    /// <summary>
    /// Berechnet die Länge des Orthodroms zwischen zwei Punkten
    /// </summary>
    /// <remarks>Versucht eine Berechnung nach WGS84</remarks>
    /// <param name="c1">Erster Punkt</param>
    /// <param name="c2">Zweiter Punkt</param>
    /// <returns>Entfernung in nautischen Meilen</returns>
    public static double DistanceNM( GeoCoordinate c1, GeoCoordinate c2 )
    {
        double dist = 0.0;

        try
        {
            dist = CalcDistanceWGS84( c1, c2, EARTHRADIUSKM ) / NMFACTOR;
        }
        catch( Exception ex )
        {
            dist = CalcDistance( c1, c2 ) * EARTHRADIUSNM;
        }

        return dist;
    }

    /// <summary>
    /// Berechnet den Kurswinkel zwischen zwei Punkten
    /// </summary>
    /// <param name="c1">Erster Punkt</param>
    /// <param name="c2">Zweiter Punkt</param>
    /// <returns>Kurswinkel in Grad (Rechtsweisend)</returns>
    public static double CourseAngle( GeoCoordinate c1, GeoCoordinate c2 )
    {
        double lat1 = GetRAD( c1.m_Latitude.DecGrad );
        double lat2 = GetRAD( c2.m_Latitude.DecGrad );

        double alpha = GetDEG( Math.Acos(
            ( Math.Sin( lat2 ) - Math.Sin( lat1 ) * Math.Cos( CalcDistance( c1, c2 ) ) ) /
            ( Math.Cos( lat1 ) * Math.Sin( CalcDistance( c1, c2 ) ) ) ) );

        if( lat1 > lat2 )
            return 360.0 - alpha;
        else
            return alpha;
    }

    #region Static Helper
    private static double CalcDistance( GeoCoordinate c1, GeoCoordinate c2 )
    {
        double lat1 = GetRAD( c1.m_Latitude.DecGrad );
        double lat2 = GetRAD( c2.m_Latitude.DecGrad );

        double lon1 = GetRAD( c1.m_Longitude.DecGrad );
        double lon2 = GetRAD( c2.m_Longitude.DecGrad );

        return Math.Acos(
            Math.Sin( lat1 ) * Math.Sin( lat2 ) +
            Math.Cos( lat1 ) * Math.Cos( lat2 ) * Math.Cos( lon2 - lon1 ) ); 
    }

    private static double CalcDistanceWGS84( GeoCoordinate c1, GeoCoordinate c2, double earthRadius )
    {
        double lat1 = c1.m_Latitude.DecGrad;
        double lat2 = c2.m_Latitude.DecGrad;

        double lon1 = c1.m_Longitude.DecGrad;
        double lon2 = c2.m_Longitude.DecGrad;

        double F = GetRAD( ( lat1 + lat2 ) / 2.0 );
        double G = GetRAD( ( lat1 - lat2 ) / 2.0 );
        double l = GetRAD( ( lon1 - lon2 ) / 2.0 );

        double S = ( Math.Pow( Math.Sin( G ), 2 ) * Math.Pow( Math.Cos( l ), 2 ) ) +
            ( Math.Pow( Math.Cos( F ), 2 ) * Math.Pow( Math.Sin( l ), 2 ) );
        double C = ( Math.Pow( Math.Cos( G ), 2 ) * Math.Pow( Math.Cos( l ), 2 ) ) +
            ( Math.Pow( Math.Sin( F ), 2 ) * Math.Pow( Math.Sin( l ), 2 ) );

        double w = Math.Atan( Math.Sqrt( S / C ) );

        double D = 2 * w * earthRadius;

        double R = ( Math.Sqrt( S * C ) ) / w;
        double H1 = ( 3 * R - 1 ) / ( 2 * C );
        double H2 = ( 3 * R + 1 ) / ( 2 * S );

        return D * (
            1 + EARTHPLATTUNG * H1 * Math.Pow( Math.Sin( F ), 2 ) * Math.Pow( Math.Cos( G ), 2 ) -
            EARTHPLATTUNG * H2 * Math.Pow( Math.Cos( F ), 2 ) * Math.Pow( Math.Sin( G ), 2 ) );
    }

    private static double GetRAD( double deg )
    {
        return deg / 180.0 * Math.PI;
    }

    private static double GetDEG( double rad )
    {
        return rad * 180.0 / Math.PI;
    }
    #endregion
    #endregion

    #region Class Methods
    /// <summary>
    /// Berechnet die Länge des Orthodroms zwischen diesem und einem anderen Punkt
    /// </summary>
    /// <remarks>Versucht eine Berechnung nach WGS84</remarks>
    /// <param name="c1">Erster Punkt</param>
    /// <returns>Entfernung in Kilometern</returns>
    /// <seealso cref="GeoCoordinate.DistanceKM"/>
    public double DistanceKM( GeoCoordinate c1 )
    {
        return GeoCoordinate.DistanceKM( this, c1 );
    }

    /// <summary>
    /// Berechnet die Länge des Orthodroms zwischen diesem und einem anderen Punkt
    /// </summary>
    /// <remarks>Versucht eine Berechnung nach WGS84</remarks>
    /// <param name="c1">Erster Punkt</param>
    /// <returns>Entfernung in nautischen Meilen</returns>
    /// <seealso cref="GeoCoordinate.DistanceNM"/>
    public double DistanceNM( GeoCoordinate c1 )
    {
        return GeoCoordinate.DistanceNM( this, c1 );
    }

    /// <summary>
    /// Berechnet den Kurswinkel zwischen diesem und einem anderen Punkt
    /// </summary>
    /// <param name="c1">Erster Punkt</param>
    /// <returns>Kurswinkel in Grad (Rechtsweisend)</returns>
    /// <seealso cref="GeoCoordinate.CourseAngle" />
    public double CourseAngle( GeoCoordinate c1 )
    {
        return GeoCoordinate.CourseAngle( this, c1 );
    }
    #endregion

    #region Overridden Methods
    public override string ToString()
    {
        return m_Latitude.ToString() + " " + m_Longitude.ToString();
    }
    #endregion

    /// <summary>
    /// Ein Koordinatenpunkt in Grad, Minuten und Sekunden
    /// </summary>
    public class GeoPoint
    {
        #region Fields
        private string m_Indicator;
        private double m_Decimal;
        #endregion

        #region Properties
        /// <summary>
        /// Dezimale Repräsentation des Punktes
        /// </summary>
        public double DecGrad
        {
            get
            {
                return m_Decimal;
            }
        }

        /// <summary>
        /// Indikator, ob N/S bzw. W/E
        /// </summary>
        public string Indicator
        {
            get
            {
                return m_Indicator;
            }
        }

        /// <summary>
        /// Die vollen Grad des Koordinatenpunktes
        /// </summary>
        public int Grad
        {
            get
            {
                return Convert.ToInt32( Math.Floor( Math.Abs( m_Decimal ) ) );
            }
        }

        /// <summary>
        /// Die Minuten des Koordinatenpunktes
        /// </summary>
        public int Minute
        {
            get
            {
                double calc = Math.Abs( m_Decimal );
                calc -= Math.Floor( calc );

                return Convert.ToInt32( Math.Floor( calc * 60.0 ) );
            }
        }

        /// <summary>
        /// Die Dezimalsekunden des Koordinatenpunktes
        /// </summary>
        public double Sekunde
        {
            get
            {
                double calc = Math.Abs( m_Decimal );
                calc -= Math.Floor( calc );
                calc -= ( Math.Floor( calc * 60.0 ) / 60.0 );

                return Math.Round( calc * 3600.0, 2 );
            }
        }
        #endregion

        #region Constructors
        /// <summary>
        /// Erstellt einen Koordinatenpunkt auf der angegebenen Halbkugel aus Dezimalgrad
        /// </summary>
        /// <param name="indicator">N/S oder W/E</param>
        /// <param name="decGrad">Koordinate in Dezimalgrad</param>
        public GeoPoint( string indicator, double decGrad )
        {
            m_Indicator = indicator;
            m_Decimal = Math.Abs( decGrad );

            switch( indicator )
            {
                case "S":
                case "E":
                    m_Decimal *= -1.0;
                    break;
            }
        }

        /// <summary>
        /// Erstellt einen Koordinatenpunkt auf der angebenen Halbkugel aus einer Gradangabe
        /// </summary>
        /// <param name="indicator">N/S oder W/E</param>
        /// <param name="grad">Die vollen Grad</param>
        /// <param name="minute">Minuten</param>
        /// <param name="sekunde">Dezimalsekunden</param>
        public GeoPoint( string indicator, int grad, int minute, double sekunde )
        {
            InitByGrad( indicator, grad, minute, sekunde );
        }

        /// <summary>
        /// Erstellt einen Koordinatenpunkt aus einem String
        /// </summary>
        /// <param name="point">
        /// <para>String in folgendem Format:</para>
        /// <para>&lt;IND&gt;&lt;GRAD&gt;[MINUTE][SEKUNDEN]</para>
        /// <para>IND: N/S/W/E (ein Zeichen)</para>
        /// <para>GRAD: Grad (2 stellig)</para>
        /// <para>MINUTE: Minuten(2 stellig)</para>
        /// <para>SEKUNDE: Sekunden(2...n stellig, ohne Komma)</para>
        /// <para></para><para>Beispiel: N6044122323</para>
        /// </param>
        public GeoPoint( string point )
        {
            string indicator = "";
            int grad = 0;
            int minute = 0;
            double sekunde = 0.0;

            if( point[ 0 ] != 'N' && point[ 0 ] != 'S' && point[ 0 ] != 'W' && point[ 0 ] != 'E' )
                throw new ArgumentException( "String must start with N,S,W or E" );

            indicator = point.Substring( 0, 1 );

            if( point.Length == 3 )
                grad = Int32.Parse( point.Substring( 1, 2 ) );

            if( point.Length == 5 )
            {
                grad = Int32.Parse( point.Substring( 1, 2 ) );
                minute = Int32.Parse( point.Substring( 3, 2 ) );
            }

            if( point.Length > 6 )
            {
                grad = Int32.Parse( point.Substring( 1, 2 ) );
                minute = Int32.Parse( point.Substring( 3, 2 ) );

                string sekString = point.Substring( 5 );
                sekunde = Double.Parse( sekString.Substring( 0, 2 ) );

                if( sekString.Length > 2 )
                    sekunde += Double.Parse( "0," + sekString.Substring( 2 ) );
            }

            InitByGrad( indicator, grad, minute, sekunde );
        }

        #endregion

        #region Private Helper
        private void InitByGrad( string indicator, int grad, int minute, double sekunde )
        {
            m_Indicator = indicator;

            if( indicator != "N" && indicator != "S" && indicator != "W" && indicator != "E" )
                throw new ArgumentException( "LOC must be N,S,W or E" );

            m_Decimal = grad + ( minute / 60.0 ) + ( sekunde / 3600.0 );

            switch( indicator )
            {
                case "S":
                case "E":
                    m_Decimal *= -1.0;
                    break;
            }
        }
        #endregion

        #region Overriden Methods
        public override string ToString()
        {
            StringBuilder sb = new StringBuilder();

            sb.Append( m_Indicator + " " );
            sb.Append( Grad + "° " );
            sb.Append( Minute + "' " );
            sb.Append( Sekunde + "''" );

            return sb.ToString();
        }
        #endregion
    }
}
Abgelegt unter Geographie, Kurswinkel, Koordinaten.

3 Kommentare zum Snippet

Jörg Dähler schrieb am 3/10/2010:
Hi Sascha, die Kurswinkelberechnung liefert falsche Ergebnisse. Ich hab es nicht weiter untersucht nur festgestellt. Wäre prima wenn du das noch mal überprüfst.

Gruß
Jörg
nasowasauch schrieb am 2/23/2011:
Hallo Sascha,

damit die Kurswinkelberechnung richtige Ergebnisse liefert muss die CourseAngle Funktion folgendermassen aussehen:

public static double CourseAngle( GeoCoordinate c1, GeoCoordinate c2 )
{
double lat1 = GetRAD( c1.m_Latitude.DecGrad );
double lat2 = GetRAD( c2.m_Latitude.DecGrad );
double long1 = GetRAD( c1.m_Longitude.DecGrad );
double long2 = GetRAD( c2.m_Longitude.DecGrad);

double alpha = GetDEG( Math.Acos(
( Math.Sin( lat2 ) - Math.Sin( lat1 ) * Math.Cos( CalcDistance( c1, c2 ) ) ) /
( Math.Cos( lat1 ) * Math.Sin( CalcDistance( c1, c2 ) ) ) ) );

if( long1 > long2 )
return 360.0 - alpha;
else
return alpha;
}
DimpiM schrieb am 5/26/2014:
Servus miteinander,
die Kursberechnung ist so auch noch nicht ganz richtig.
Laut Wikipedia (http://de.wikipedia.org/wiki/Kurswinkel) muss die Distanz nochmal durch den Erdradius (6378137) geteilt werden...
D.h.

double alpha = GetDEG(Math.Acos(
(Math.Sin(lat2) - Math.Sin(lat1) * Math.Cos(CalcDistance(c1, c2) / Double.Parse("6378137"))) /
(Math.Cos(lat1) * Math.Sin(CalcDistance(c1, c2) / Double.Parse("6378137")))));

 

Logge dich ein, um hier zu kommentieren!