2019-06-28 11:14:53 +02:00
using System ;
using System.ComponentModel ;
2019-12-09 14:46:34 +01:00
using System.Diagnostics ;
using System.Linq ;
2019-06-28 11:14:53 +02:00
2019-12-09 14:46:34 +01:00
namespace CoordinateSharp {
/// <summary>
/// Military Grid Reference System (MGRS). Uses the WGS 84 Datum.
/// Relies upon values from the UniversalTransverseMercator class
/// </summary>
[Serializable]
public class MilitaryGridReferenceSystem : INotifyPropertyChanged {
2019-06-28 11:14:53 +02:00
/// <summary>
2019-12-09 14:46:34 +01:00
/// Create an MGRS object with WGS84 datum
2019-06-28 11:14:53 +02:00
/// </summary>
2019-12-09 14:46:34 +01:00
/// <param name="latz">Lat Zone</param>
/// <param name="longz">Long Zone</param>
/// <param name="d">Digraph</param>
/// <param name="e">Easting</param>
/// <param name="n">Northing</param>
public MilitaryGridReferenceSystem ( String latz , Int32 longz , String d , Double e , Double n ) {
String digraphLettersE = "ABCDEFGHJKLMNPQRSTUVWXYZ" ;
String digraphLettersN = "ABCDEFGHJKLMNPQRSTUV" ;
if ( longz < 1 | | longz > 60 ) { Debug . WriteLine ( "Longitudinal zone out of range" , "UTM longitudinal zones must be between 1-60." ) ; }
if ( ! this . Verify_Lat_Zone ( latz ) ) { throw new ArgumentException ( "Latitudinal zone invalid" , "UTM latitudinal zone was unrecognized." ) ; }
if ( n < 0 | | n > 10000000 ) { throw new ArgumentOutOfRangeException ( "Northing out of range" , "Northing must be between 0-10,000,000." ) ; }
if ( d . Count ( ) < 2 | | d . Count ( ) > 2 ) { throw new ArgumentException ( "Digraph invalid" , "MGRS Digraph was unrecognized." ) ; }
if ( digraphLettersE . ToCharArray ( ) . ToList ( ) . Where ( x = > x . ToString ( ) = = d . ToUpper ( ) [ 0 ] . ToString ( ) ) . Count ( ) = = 0 ) { throw new ArgumentException ( "Digraph invalid" , "MGRS Digraph was unrecognized." ) ; }
if ( digraphLettersN . ToCharArray ( ) . ToList ( ) . Where ( x = > x . ToString ( ) = = d . ToUpper ( ) [ 1 ] . ToString ( ) ) . Count ( ) = = 0 ) { throw new ArgumentException ( "Digraph invalid" , "MGRS Digraph was unrecognized." ) ; }
this . LatZone = latz ;
this . LongZone = longz ;
this . Digraph = d ;
this . Easting = e ;
this . Northing = n ;
//WGS84
this . equatorialRadius = 6378137.0 ;
this . inverseFlattening = 298.257223563 ;
2019-06-28 11:14:53 +02:00
2019-12-09 14:46:34 +01:00
}
/// <summary>
/// Create an MGRS object with custom datum
/// </summary>
/// <param name="latz">Lat Zone</param>
/// <param name="longz">Long Zone</param>
/// <param name="d">Digraph</param>
/// <param name="e">Easting</param>
/// <param name="n">Northing</param>
/// <param name="rad">Equatorial Radius</param>
/// <param name="flt">Inverse Flattening</param>
public MilitaryGridReferenceSystem ( String latz , Int32 longz , String d , Double e , Double n , Double rad , Double flt ) {
String digraphLettersE = "ABCDEFGHJKLMNPQRSTUVWXYZ" ;
String digraphLettersN = "ABCDEFGHJKLMNPQRSTUV" ;
if ( longz < 1 | | longz > 60 ) { Debug . WriteLine ( "Longitudinal zone out of range" , "UTM longitudinal zones must be between 1-60." ) ; }
if ( ! this . Verify_Lat_Zone ( latz ) ) { throw new ArgumentException ( "Latitudinal zone invalid" , "UTM latitudinal zone was unrecognized." ) ; }
if ( n < 0 | | n > 10000000 ) { throw new ArgumentOutOfRangeException ( "Northing out of range" , "Northing must be between 0-10,000,000." ) ; }
if ( d . Count ( ) < 2 | | d . Count ( ) > 2 ) { throw new ArgumentException ( "Digraph invalid" , "MGRS Digraph was unrecognized." ) ; }
if ( digraphLettersE . ToCharArray ( ) . ToList ( ) . Where ( x = > x . ToString ( ) = = d . ToUpper ( ) [ 0 ] . ToString ( ) ) . Count ( ) = = 0 ) { throw new ArgumentException ( "Digraph invalid" , "MGRS Digraph was unrecognized." ) ; }
if ( digraphLettersN . ToCharArray ( ) . ToList ( ) . Where ( x = > x . ToString ( ) = = d . ToUpper ( ) [ 1 ] . ToString ( ) ) . Count ( ) = = 0 ) { throw new ArgumentException ( "Digraph invalid" , "MGRS Digraph was unrecognized." ) ; }
this . LatZone = latz ;
this . LongZone = longz ;
this . Digraph = d ;
this . Easting = e ;
this . Northing = n ;
this . equatorialRadius = rad ;
this . inverseFlattening = flt ;
}
2019-06-28 11:14:53 +02:00
2019-12-09 14:46:34 +01:00
private Double equatorialRadius ;
private Double inverseFlattening ;
2019-06-28 11:14:53 +02:00
2019-12-09 14:46:34 +01:00
private Boolean Verify_Lat_Zone ( String l ) = > LatZones . longZongLetters . Where ( x = > x = = l . ToUpper ( ) ) . Count ( ) = = 1 ;
2019-06-28 11:14:53 +02:00
2019-12-09 14:46:34 +01:00
/// <summary>
/// MGRS Zone Letter
/// </summary>
public String LatZone { get ; private set ; }
/// <summary>
/// MGRS Zone Number
/// </summary>
public Int32 LongZone { get ; private set ; }
/// <summary>
/// MGRS Easting
/// </summary>
public Double Easting { get ; private set ; }
/// <summary>
/// MGRS Northing
/// </summary>
public Double Northing { get ; private set ; }
/// <summary>
/// MGRS Digraph
/// </summary>
public String Digraph { get ; private set ; }
/// <summary>
/// Is MGRS conversion within the coordinate system's accurate boundaries after conversion from Lat/Long.
/// </summary>
public Boolean WithinCoordinateSystemBounds { get ; private set ; } = true ;
2019-06-28 11:14:53 +02:00
2019-12-09 14:46:34 +01:00
internal MilitaryGridReferenceSystem ( UniversalTransverseMercator utm ) = > this . ToMGRS ( utm ) ;
internal void ToMGRS ( UniversalTransverseMercator utm ) {
Digraphs digraphs = new Digraphs ( ) ;
2019-06-28 11:14:53 +02:00
2019-12-09 14:46:34 +01:00
String digraph1 = digraphs . GetDigraph1 ( utm . LongZone , utm . Easting ) ;
String digraph2 = digraphs . GetDigraph2 ( utm . LongZone , utm . Northing ) ;
2019-06-28 11:14:53 +02:00
2019-12-09 14:46:34 +01:00
this . Digraph = digraph1 + digraph2 ;
this . LatZone = utm . LatZone ;
this . LongZone = utm . LongZone ;
2019-06-28 11:14:53 +02:00
2019-12-09 14:46:34 +01:00
//String easting = String.valueOf((int)_easting);
String e = ( ( Int32 ) utm . Easting ) . ToString ( ) ;
if ( e . Length < 5 ) {
e = "00000" + ( ( Int32 ) utm . Easting ) . ToString ( ) ;
}
e = e . Substring ( e . Length - 5 ) ;
2019-06-28 11:14:53 +02:00
2019-12-09 14:46:34 +01:00
this . Easting = Convert . ToInt32 ( e ) ;
2019-06-28 11:14:53 +02:00
2019-12-09 14:46:34 +01:00
String n = ( ( Int32 ) utm . Northing ) . ToString ( ) ;
if ( n . Length < 5 ) {
n = "0000" + ( ( Int32 ) utm . Northing ) . ToString ( ) ;
}
n = n . Substring ( n . Length - 5 ) ;
2019-06-28 11:14:53 +02:00
2019-12-09 14:46:34 +01:00
this . Northing = Convert . ToInt32 ( n ) ;
this . equatorialRadius = utm . equatorial_radius ;
this . inverseFlattening = utm . inverse_flattening ;
2019-06-28 11:14:53 +02:00
2019-12-09 14:46:34 +01:00
this . WithinCoordinateSystemBounds = utm . WithinCoordinateSystemBounds ;
2019-06-28 11:14:53 +02:00
}
2019-12-09 14:46:34 +01:00
/// <summary>
/// Creates a Coordinate object from an MGRS/NATO UTM Coordinate
/// </summary>
/// <param name="mgrs">MilitaryGridReferenceSystem</param>
/// <returns>Coordinate object</returns>
public static Coordinate MGRStoLatLong ( MilitaryGridReferenceSystem mgrs ) {
String latz = mgrs . LatZone ;
String digraph = mgrs . Digraph ;
Char eltr = digraph [ 0 ] ;
Char nltr = digraph [ 1 ] ;
String digraphLettersE = "ABCDEFGHJKLMNPQRSTUVWXYZ" ;
//String digraphLettersN = "ABCDEFGHJKLMNPQRSTUV";
String digraphLettersAll = "" ;
for ( Int32 lt = 1 ; lt < 25 ; lt + + ) {
digraphLettersAll + = "ABCDEFGHJKLMNPQRSTUV" ;
}
Int32 eidx = digraphLettersE . IndexOf ( eltr ) ;
//Int32 nidx = digraphLettersN.IndexOf(nltr);
if ( mgrs . LongZone / 2.0 = = Math . Floor ( mgrs . LongZone / 2.0 ) ) {
//nidx -= 5; // correction for even numbered zones
}
Double ebase = 100000 * ( 1 + eidx - 8 * Math . Floor ( Convert . ToDouble ( eidx ) / 8 ) ) ;
Int32 latBand = digraphLettersE . IndexOf ( latz ) ;
Int32 latBandLow = 8 * latBand - 96 ;
Int32 latBandHigh = 8 * latBand - 88 ;
if ( latBand < 2 ) {
latBandLow = - 90 ;
latBandHigh = - 80 ;
} else if ( latBand = = 21 ) {
latBandLow = 72 ;
latBandHigh = 84 ;
} else if ( latBand > 21 ) {
latBandLow = 84 ;
latBandHigh = 90 ;
}
Double lowLetter = Math . Floor ( 100 + 1.11 * latBandLow ) ;
Double highLetter = Math . Round ( 100 + 1.11 * latBandHigh ) ;
Int32 l = Convert . ToInt32 ( lowLetter ) ;
Int32 h = Convert . ToInt32 ( highLetter ) ;
String latBandLetters = mgrs . LongZone / 2.0 = = Math . Floor ( mgrs . LongZone / 2.0 ) ? digraphLettersAll . Substring ( l + 5 , h + 5 ) . ToString ( ) : digraphLettersAll . Substring ( l , h ) . ToString ( ) ;
Double nbase = 100000 * ( lowLetter + latBandLetters . IndexOf ( nltr ) ) ;
//latBandLetters.IndexOf(nltr) value causing incorrect Northing below -80
Double x = ebase + mgrs . Easting ;
Double y = nbase + mgrs . Northing ;
if ( y > 10000000 ) {
y - = 10000000 ;
}
if ( nbase > = 10000000 ) {
y = nbase + mgrs . Northing - 10000000 ;
}
_ = nbase < 10000000 ;
UniversalTransverseMercator utm = new UniversalTransverseMercator ( mgrs . LatZone , mgrs . LongZone , x , y ) {
equatorial_radius = mgrs . equatorialRadius ,
inverse_flattening = mgrs . inverseFlattening
} ;
Coordinate c = UniversalTransverseMercator . ConvertUTMtoLatLong ( utm ) ;
c . Set_Datum ( mgrs . equatorialRadius , mgrs . inverseFlattening ) ;
return c ;
}
/// <summary>
/// MGRS Default String Format
/// </summary>
/// <returns>MGRS Formatted Coordinate String</returns>
public override String ToString ( ) = > ! this . WithinCoordinateSystemBounds ? "" : this . LongZone . ToString ( ) + this . LatZone + " " + this . Digraph + " " + ( ( Int32 ) this . Easting ) . ToString ( "00000" ) + " " + ( ( Int32 ) this . Northing ) . ToString ( "00000" ) ;
//MGRS Coordinate is outside its reliable boundaries. Return empty.
/// <summary>
/// Property changed event
/// </summary>
public event PropertyChangedEventHandler PropertyChanged ;
/// <summary>
/// Notify property changed
/// </summary>
/// <param name="propName">Property name</param>
public void NotifyPropertyChanged ( String propName ) = > PropertyChanged ? . Invoke ( this , new PropertyChangedEventArgs ( propName ) ) ;
}
2019-06-28 11:14:53 +02:00
}