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
|
using CoordinateSharp;
using System.ComponentModel.DataAnnotations.Schema;
using System.Text.Json.Serialization;
using System.Text.RegularExpressions;
namespace PagerParser;
public class GpsPosition {
[JsonIgnore]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int GpsPositionId { get; set; }
public double Latitude { get; set; }
public double Longitude { get; set; }
private const double EarthRadius = 6_371;
/// <summary>
/// Get the distance from this point to the specified coordinates
/// </summary>
public double GetDistance(double latitude, double longitude) =>
Math.Acos(
Math.Sin(Latitude) * Math.Sin(latitude) +
Math.Cos(Latitude) * Math.Cos(latitude) *
Math.Cos(longitude - Longitude)) * EarthRadius;
/// <summary>
/// Get the distance from this point to another
/// </summary>
public double GetDistance(GpsPosition position) =>
GetDistance(position.Latitude, position.Longitude);
}
public record MelwaysPage {
public int PageNo { get; set; }
public double Latitude { get; set; }
public double Longitude { get; set; }
}
// Derive roughly-accurate GPS coordinates from a Melways page number
// and grid number (e.g: 131 D7) by looking up the origin coordinates
// of the Melways page from a pre-defined table, then interpolating
// the grid number across the page to get a (very) rough position.
public class PositionCalculator {
private const float PageWidth = 0.0583403752108005f;
private const float PageHeight = 0.0444074086328539f;
private const int ColCount = 10;
private const int RowCount = 12;
private const float ColWidth = PageWidth / ColCount;
private const float RowHeight = PageHeight / RowCount;
private static readonly MelwaysPage[] MelwaysPages = {
new MelwaysPage { PageNo = 94, Latitude = -38.0062431030181, Longitude = 145.153465677450 },
new MelwaysPage { PageNo = 95, Latitude = -38.0061349929752, Longitude = 145.197873798763 },
new MelwaysPage { PageNo = 96, Latitude = -38.0060458242193, Longitude = 145.242282747795 },
new MelwaysPage { PageNo = 98, Latitude = -38.0501502394990, Longitude = 145.153538108815 },
new MelwaysPage { PageNo = 109, Latitude = -37.9620542882551, Longitude = 145.330988175988 },
new MelwaysPage { PageNo = 110, Latitude = -38.0059397267576, Longitude = 145.286740799198 },
new MelwaysPage { PageNo = 111, Latitude = -38.0058646642328, Longitude = 145.331164408607 },
new MelwaysPage { PageNo = 128, Latitude = -38.0500712736303, Longitude = 145.197997939698 },
new MelwaysPage { PageNo = 129, Latitude = -38.0499811306888, Longitude = 145.242424322651 },
new MelwaysPage { PageNo = 130, Latitude = -38.0498760059982, Longitude = 145.286857218675 },
new MelwaysPage { PageNo = 131, Latitude = -38.0497905233513, Longitude = 145.331303121527 },
new MelwaysPage { PageNo = 134, Latitude = -38.0938684113233, Longitude = 145.287035643320 },
new MelwaysPage { PageNo = 135, Latitude = -38.0938684113233, Longitude = 145.331502355613 },
new MelwaysPage { PageNo = 212, Latitude = -38.0056782419614, Longitude = 145.375580424547 },
new MelwaysPage { PageNo = 213, Latitude = -38.0055063939227, Longitude = 145.419999796841 },
new MelwaysPage { PageNo = 214, Latitude = -38.0496145139129, Longitude = 145.375744441368 },
new MelwaysPage { PageNo = 215, Latitude = -38.0494726543543, Longitude = 145.420190429893 },
new MelwaysPage { PageNo = 315, Latitude = -38.0053618479218, Longitude = 145.464319398407 },
new MelwaysPage { PageNo = 317, Latitude = -38.0493097638301, Longitude = 145.464666307851 }
};
public static GpsPosition? GetGpsPosition(ParsedPagerMessage message) {
if(message.MapType != MapType.Melways)
return null;
if(message.MapNo is null || message.MapGrid is null)
return null;
var page = MelwaysPages.FirstOrDefault(p => p.PageNo == message.MapNo);
if(page is null)
return null;
var match = Regex.Match(message.MapGrid.ToUpper(), "^([A-HJ-K])([0-9]+)$");
if(!match.Success)
return null;
var row = int.Parse(match.Groups[2].Value) - 1;
var col = match.Groups[1].Value[0] - 'A';
if(col > 7)
col -= 1; // Compensate for Melways grids skipping 'I'
var position = new GpsPosition {
Latitude = page.Latitude - (row * RowHeight) - (RowHeight / 2),
Longitude = page.Longitude + (col * ColWidth) + (ColWidth / 2)
};
if(message.GridReference is null)
return position;
// If we have a 6-digit grid reference, try to refine the location further
// using the UTM coordinate system. Since zone information isn't included
// in a 6-digit grid reference, we use the coordinates calculated from the
// Melways as a starting point to find the zone.
double gridRef = (double) message.GridReference;
var origUTM = new Coordinate(position.Latitude, position.Longitude).UTM;
var newUTM = new UniversalTransverseMercator(
origUTM.LatZone,
origUTM.LongZone,
(Math.Floor(origUTM.Easting / 100000) * 100000) + (Math.Floor(gridRef / 1000) * 100),
(Math.Floor(origUTM.Northing / 100000) * 100000) + ((gridRef % 1000) * 100));
var coord = UniversalTransverseMercator.ConvertUTMtoLatLong(newUTM);
position.Latitude = coord.Latitude.ToDouble();
position.Longitude = coord.Longitude.ToDouble();
return position;
}
}
|