-
Notifications
You must be signed in to change notification settings - Fork 0
/
MaidenheadLocator.cs
299 lines (268 loc) · 9.39 KB
/
MaidenheadLocator.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
// Copyright (c) 2011, Yves Goergen, http://unclassified.software/source/maidenheadlocator
//
// Copying and distribution of this file, with or without modification, are permitted provided the
// copyright notice and this notice are preserved. This file is offered as-is, without any warranty.
// This class is based on a Perl module by Dirk Koopman, G1TLH, from 2002-11-07.
// Source: http://www.koders.com/perl/fidDAB6FD208AC4F5C0306CA344485FD0899BD2F328.aspx
using System;
using System.Text.RegularExpressions;
namespace Unclassified.Util
{
/// <summary>
/// Class providing static methods for calculating with Maidenhead locators, especially
/// distance and bearing.
/// </summary>
public class MaidenheadLocator
{
/// <summary>
/// Convert a locator to latitude and longitude in degrees
/// </summary>
/// <param name="locator">Locator string to convert</param>
/// <returns>LatLng structure</returns>
public static LatLng LocatorToLatLng(string locator)
{
locator = locator.Trim().ToUpper();
if (Regex.IsMatch(locator, "^[A-R]{2}[0-9]{2}$"))
{
LatLng ll = new LatLng
{
Long = (locator[0] - 'A') * 20 + (locator[2] - '0' + 0.5) * 2 - 180,
Lat = (locator[1] - 'A') * 10 + (locator[3] - '0' + 0.5) - 90
};
return ll;
}
else if (Regex.IsMatch(locator, "^[A-R]{2}[0-9]{2}[A-X]{2}$"))
{
LatLng ll = new LatLng
{
Long = (locator[0] - 'A') * 20 + (locator[2] - '0') * 2 + (locator[4] - 'A' + 0.5) / 12 - 180,
Lat = (locator[1] - 'A') * 10 + (locator[3] - '0') + (locator[5] - 'A' + 0.5) / 24 - 90
};
return ll;
}
else if (Regex.IsMatch(locator, "^[A-R]{2}[0-9]{2}[A-X]{2}[0-9]{2}$"))
{
LatLng ll = new LatLng
{
Long = (locator[0] - 'A') * 20 + (locator[2] - '0') * 2 + (locator[4] - 'A' + 0.0) / 12 + (locator[6] - '0' + 0.5) / 120 - 180,
Lat = (locator[1] - 'A') * 10 + (locator[3] - '0') + (locator[5] - 'A' + 0.0) / 24 + (locator[7] - '0' + 0.5) / 240 - 90
};
return ll;
}
else if (Regex.IsMatch(locator, "^[A-R]{2}[0-9]{2}[A-X]{2}[0-9]{2}[A-X]{2}$"))
{
LatLng ll = new LatLng();
ll.Long = (locator[0] - 'A') * 20 + (locator[2] - '0') * 2 + (locator[4] - 'A' + 0.0) / 12 + (locator[6] - '0' + 0.0) / 120 + (locator[8] - 'A' + 0.5) / 120 / 24 - 180;
ll.Lat = (locator[1] - 'A') * 10 + (locator[3] - '0') + (locator[5] - 'A' + 0.0) / 24 + (locator[7] - '0' + 0.0) / 240 + (locator[9] - 'A' + 0.5) / 240 / 24 - 90;
return ll;
}
else
{
throw new FormatException("Invalid locator format");
}
}
/// <summary>
/// Convert latitude and longitude in degrees to a locator
/// </summary>
/// <param name="ll">LatLng structure to convert</param>
/// <returns>Locator string</returns>
public static string LatLngToLocator(LatLng ll)
{
return LatLngToLocator(ll.Lat, ll.Long, 0);
}
/// <summary>
/// Convert latitude and longitude in degrees to a locator
/// </summary>
/// <param name="ll">LatLng structure to convert</param>
/// <param name="Ext">Extra precision (0, 1, 2)</param>
/// <returns>Locator string</returns>
public static string LatLngToLocator(LatLng ll, int Ext)
{
return LatLngToLocator(ll.Lat, ll.Long, Ext);
}
/// <summary>
/// Convert latitude and longitude in degrees to a locator
/// </summary>
/// <param name="Lat">Latitude to convert</param>
/// <param name="Long">Longitude to convert</param>
/// <returns>Locator string</returns>
public static string LatLngToLocator(double Lat, double Long)
{
return LatLngToLocator(Lat, Long, 0);
}
/// <summary>
/// Convert latitude and longitude in degrees to a locator
/// </summary>
/// <param name="Lat">Latitude to convert</param>
/// <param name="Long">Longitude to convert</param>
/// <param name="Ext">Extra precision (0, 1, 2)</param>
/// <returns>Locator string</returns>
public static string LatLngToLocator(double Lat, double Long, int Ext)
{
int v;
string locator = "";
Lat += 90;
Long += 180;
locator += (char)('A' + Math.Floor(Long / 20));
locator += (char)('A' + Math.Floor(Lat / 10));
Long = Math.IEEERemainder(Long, 20);
if (Long < 0) Long += 20;
Lat = Math.IEEERemainder(Lat, 10);
if (Lat < 0) Lat += 10;
locator += (char)('0' + Math.Floor(Long / 2));
locator += (char)('0' + Math.Floor(Lat / 1));
Long = Math.IEEERemainder(Long, 2);
if (Long < 0) Long += 2;
Lat = Math.IEEERemainder(Lat, 1);
if (Lat < 0) Lat += 1;
locator += (char)('A' + Math.Floor(Long * 12));
locator += (char)('A' + Math.Floor(Lat * 24));
Long = Math.IEEERemainder(Long, (double)1 / 12);
if (Long < 0) Long += (double)1 / 12;
Lat = Math.IEEERemainder(Lat, (double)1 / 24);
if (Lat < 0) Lat += (double)1 / 24;
if (Ext >= 1)
{
locator += (char)('0' + Math.Floor(Long * 120));
locator += (char)('0' + Math.Floor(Lat * 240));
Long = Math.IEEERemainder(Long, (double)1 / 120);
if (Long < 0) Long += (double)1 / 120;
Lat = Math.IEEERemainder(Lat, (double)1 / 240);
if (Lat < 0) Lat += (double)1 / 240;
}
if (Ext >= 2)
{
locator += (char)('A' + Math.Floor(Long * 120 * 24));
locator += (char)('A' + Math.Floor(Lat * 240 * 24));
Long = Math.IEEERemainder(Long, (double)1 / 120 / 24);
if (Long < 0) Long += (double)1 / 120 / 24;
Lat = Math.IEEERemainder(Lat, (double)1 / 240 / 24);
if (Lat < 0) Lat += (double)1 / 240 / 24;
}
return locator;
//Lat += 90;
//Long += 180;
//v = (int) (Long / 20);
//Long -= v * 20;
//locator += (char) ('A' + v);
//v = (int) (Lat / 10);
//Lat -= v * 10;
//locator += (char) ('A' + v);
//locator += ((int) (Long / 2)).ToString();
//locator += ((int) Lat).ToString();
//Long -= (int) (Long / 2) * 2;
//Lat -= (int) Lat;
//locator += (char) ('A' + Long * 12);
//locator += (char) ('A' + Lat * 24);
//return locator;
}
/// <summary>
/// Convert radians to degrees
/// </summary>
/// <param name="rad"></param>
/// <returns></returns>
public static double RadToDeg(double rad)
{
return rad / Math.PI * 180;
}
/// <summary>
/// Convert degrees to radians
/// </summary>
/// <param name="deg"></param>
/// <returns></returns>
public static double DegToRad(double deg)
{
return deg / 180 * Math.PI;
}
/// <summary>
/// Calculate the distance in km between two locators
/// </summary>
/// <param name="A">Start locator string</param>
/// <param name="B">End locator string</param>
/// <returns>Distance in km</returns>
public static double Distance(string A, string B)
{
return Distance(LocatorToLatLng(A), LocatorToLatLng(B));
}
/// <summary>
/// Calculate the distance in km between two locators
/// </summary>
/// <param name="A">Start LatLng structure</param>
/// <param name="B">End LatLng structure</param>
/// <returns>Distance in km</returns>
public static double Distance(LatLng A, LatLng B)
{
if (A.CompareTo(B) == 0) return 0;
double hn = DegToRad(A.Lat);
double he = DegToRad(A.Long);
double n = DegToRad(B.Lat);
double e = DegToRad(B.Long);
double co = Math.Cos(he - e) * Math.Cos(hn) * Math.Cos(n) + Math.Sin(hn) * Math.Sin(n);
double ca = Math.Atan(Math.Abs(Math.Sqrt(1 - co * co) / co));
if (co < 0) ca = Math.PI - ca;
double dx = 6367 * ca;
return dx;
}
/// <summary>
/// Calculate the azimuth in degrees between two locators
/// </summary>
/// <param name="A">Start locator string</param>
/// <param name="B">End locator string</param>
/// <returns>Azimuth in degrees</returns>
public static double Azimuth(string A, string B)
{
return Azimuth(LocatorToLatLng(A), LocatorToLatLng(B));
}
/// <summary>
/// Calculate the azimuth in degrees between two locators
/// </summary>
/// <param name="A">Start LatLng structure</param>
/// <param name="B">End LatLng structure</param>
/// <returns>Azimuth in degrees</returns>
public static double Azimuth(LatLng A, LatLng B)
{
if (A.CompareTo(B) == 0) return 0;
double hn = DegToRad(A.Lat);
double he = DegToRad(A.Long);
double n = DegToRad(B.Lat);
double e = DegToRad(B.Long);
double co = Math.Cos(he - e) * Math.Cos(hn) * Math.Cos(n) + Math.Sin(hn) * Math.Sin(n);
double ca = Math.Atan(Math.Abs(Math.Sqrt(1 - co * co) / co));
if (co < 0) ca = Math.PI - ca;
double si = Math.Sin(e - he) * Math.Cos(n) * Math.Cos(hn);
co = Math.Sin(n) - Math.Sin(hn) * Math.Cos(ca);
double az = Math.Atan(Math.Abs(si / co));
if (co < 0) az = Math.PI - az;
if (si < 0) az = -az;
if (az < 0) az = az + 2 * Math.PI;
return RadToDeg(az);
}
}
/// <summary>
/// Simple structure to store a position in latitude and longitude
/// </summary>
public struct LatLng : IComparable
{
/// <summary>
/// Latitude, -90 to +90 (N/S direction)
/// </summary>
public double Lat;
/// <summary>
/// Longitude, -180 to +180 (W/E direction)
/// </summary>
public double Long;
public override string ToString()
{
return Long.ToString("#.###") + (Long >= 0 ? "N" : "S") + " " + Lat.ToString("#.###") + (Lat >= 0 ? "E" : "W");
}
public int CompareTo(object to)
{
if (to is LatLng)
{
if (Lat == ((LatLng)to).Lat && Long == ((LatLng)to).Long) return 0;
return -1;
}
return -1;
}
}
}