Skip to content

Commit

Permalink
feat: arcgen
Browse files Browse the repository at this point in the history
Fixes #132 - Implement ArcGen
  • Loading branch information
AndyTWF authored Sep 13, 2023
2 parents af08520 + 0b738af commit c01cfce
Show file tree
Hide file tree
Showing 9 changed files with 241 additions and 5 deletions.
11 changes: 11 additions & 0 deletions src/Compiler/Input/AbstractSectorDataReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,17 @@ public bool IsBlankLine(string line)
return line.Trim() == "";
}

/*
* Returns whether or not the line is an arc gen line
*/
public bool IsArcGenLine(string line) {
try {
return line.TrimStart().StartsWith("@ARC");
} catch (ArgumentOutOfRangeException) {
return false;
}
}

/*
* Returns whether or not the line is a comment line
*/
Expand Down
116 changes: 115 additions & 1 deletion src/Compiler/Input/SectorDataFile.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
using Compiler.Model;

namespace Compiler.Input
Expand Down Expand Up @@ -39,6 +41,118 @@ public override IEnumerator<SectorData> GetEnumerator()
{
docblock.AddLine(reader.GetCommentSegment(line));
}
else if (reader.IsArcGenLine(line)) {
// Return new SectorData for each new region

const int DELTA_THETA = 5;
const double R = 6372.795477598;

Regex rx = new Regex(@"@ARC\(region (.*) centre ([NS]\d{3}\.\d{2}\.\d{2}\.\d{3}) ([EW]\d{3}\.\d{2}\.\d{2}\.\d{3}) radius (\d*(?:\.\d*){0,1})(?: from ([NS]\d{3}\.\d{2}\.\d{2}\.\d{3}) ([EW]\d{3}\.\d{2}\.\d{2}\.\d{3}) to ([NS]\d{3}\.\d{2}\.\d{2}\.\d{3}) ([EW]\d{3}\.\d{2}\.\d{2}\.\d{3})){0,1}\)", RegexOptions.None);
GroupCollection groups = rx.Match(line).Groups; // error catching!

string regionName = groups[1].Value;

double lat = Coordinate.DegreeMinSecToDecimalDegree(groups[2].Value);
double lon = Coordinate.DegreeMinSecToDecimalDegree(groups[3].Value);

float radius = float.Parse(groups[4].Value);

int initialTheta = 0;
int finalTheta = 360;

string prevLat = "";
string prevLon = "";

bool includesFromTo = false;

if (groups.Count > 5) { // includes a from / to as
if (groups[5].Value.Length > 0) {
includesFromTo = true;
prevLat = groups[5].Value;
prevLon = groups[6].Value;

double fromLat = Coordinate.DegreeMinSecToDecimalDegree(groups[5].Value);
double fromLon = Coordinate.DegreeMinSecToDecimalDegree(groups[6].Value);

double toLat = Coordinate.DegreeMinSecToDecimalDegree(groups[7].Value);
double toLon = Coordinate.DegreeMinSecToDecimalDegree(groups[8].Value);

// Calculate angle to from / to coordinate

double fromTheta = Math.Atan2(Math.Cos(fromLat * Math.PI / 180) * Math.Sin((fromLon - lon) * Math.PI / 180),
Math.Cos(lat * Math.PI / 180) * Math.Sin(fromLat * Math.PI / 180) - Math.Sin(lat * Math.PI / 180) * Math.Cos(fromLat * Math.PI / 180) * Math.Cos((fromLon - lon) * Math.PI / 180)
);
fromTheta = (180 * fromTheta / Math.PI + 360) % 360;

double toTheta = Math.Atan2(Math.Cos(toLat * Math.PI / 180) * Math.Sin((toLon - lon) * Math.PI / 180),
Math.Cos(lat * Math.PI / 180) * Math.Sin(toLat * Math.PI / 180) - Math.Sin(lat * Math.PI / 180) * Math.Cos(toLat * Math.PI / 180) * Math.Cos((toLon - lon) * Math.PI / 180)
);
toTheta = (180 * toTheta / Math.PI + 360) % 360;

initialTheta = (int)Math.Min(fromTheta, toTheta);
finalTheta = (int)Math.Max(fromTheta, toTheta);

initialTheta += 1; // padding
finalTheta -= DELTA_THETA;

// calculate actual radius

double fromDist = R * Math.Acos(Math.Sin(lat * Math.PI / 180) * Math.Sin(fromLat * Math.PI / 180) + Math.Cos(lat * Math.PI / 180) * Math.Cos(fromLat * Math.PI / 180) * Math.Cos((lon - fromLon) * Math.PI / 180));
fromDist = fromDist / 1.852;
double toDist = R * Math.Acos(Math.Sin(lat * Math.PI / 180) * Math.Sin(toLat * Math.PI / 180) + Math.Cos(lat * Math.PI / 180) * Math.Cos(toLat * Math.PI / 180) * Math.Cos((lon - toLon) * Math.PI / 180));
toDist = toDist / 1.852;

float meanRadius = (float)Math.Round((fromDist + toDist) / 2, 2);

if (meanRadius != radius) {
radius = meanRadius; // AIP radius was wrong
}
}
}

for (int theta = initialTheta; theta <= finalTheta; theta += DELTA_THETA) {
double deltaLat = (radius * Math.Cos(theta * Math.PI / 180)) / 60.0d;
double deltaLon = (radius * Math.Sin(theta * Math.PI / 180)) / 60.0d;
deltaLon /= Math.Cos((lat) * Math.PI / 180); // account for length of nautical mile changing with latitude

string newLat = Coordinate.DecimalDegreeToDegreeMinSec(lat + deltaLat, true);
string newLon = Coordinate.DecimalDegreeToDegreeMinSec(lon + deltaLon, false);

if (theta == initialTheta && !includesFromTo) {
prevLat = newLat;
prevLon = newLon;
continue;
}

string outLine = $"{regionName} {prevLat} {prevLon} {newLat} {newLon}";

prevLat = newLat;
prevLon = newLon;

// For each new coordinate, yield return it.

yield return new SectorData(
docblock,
reader.GetCommentSegment(outLine),
reader.GetDataSegments(outLine),
reader.GetRawData(outLine),
new Definition(this.FullPath, this.CurrentLineNumber) // sus
);
docblock = new Docblock();
}

if (includesFromTo) {
string outLine = $"{regionName} {prevLat} {prevLon} {groups[7].Value} {groups[8].Value}";
yield return new SectorData(
docblock,
reader.GetCommentSegment(outLine),
reader.GetDataSegments(outLine),
reader.GetRawData(outLine),
new Definition(this.FullPath, this.CurrentLineNumber) // sus
);
docblock = new Docblock();
}
}
else
{
yield return new SectorData(
Expand Down
43 changes: 42 additions & 1 deletion src/Compiler/Model/Coordinate.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
namespace Compiler.Model
using System;

namespace Compiler.Model
{
public struct Coordinate
{
Expand All @@ -11,6 +13,45 @@ public Coordinate(string latitude, string longitude)
this.longitude = longitude;
}

public static double DegreeMinSecToDecimalDegree(string latOrLong) {
double output = 0;
string[] sections = latOrLong.Split('.');
output += int.Parse(sections[0].Substring(1));
output += int.Parse(sections[1]) / 60.0d;
output += int.Parse(sections[2]) / 3600.0d;
output += int.Parse(sections[3]) / 3600000.0d;
if (sections[0].StartsWith("S") || sections[0].StartsWith("W")) {
output = -output;
}
return output;
}

public static string DecimalDegreeToDegreeMinSec(double decimalDegree, bool isLat) {
string output = "";
if (decimalDegree > 0) {
if (isLat) output += "N";
else output += "E";
} else {
decimalDegree = -decimalDegree;
if (isLat) output += "S";
else output += "W";
}

output += ((int)decimalDegree).ToString().PadLeft(3, '0') + ".";
decimalDegree -= (int)decimalDegree;
decimalDegree *= 60;
output += ((int)decimalDegree).ToString().PadLeft(2, '0') + ".";
decimalDegree -= (int)decimalDegree;
decimalDegree *= 60;
output += ((int)decimalDegree).ToString().PadLeft(2, '0') + ".";
decimalDegree -= (int)decimalDegree;
decimalDegree *= 1000;
if (Math.Round(decimalDegree) > 999) output += "999";
else output += Math.Round(decimalDegree).ToString().PadLeft(3, '0');

return output;
}

public override bool Equals(object obj)
{
return (obj is Coordinate) &&
Expand Down
1 change: 1 addition & 0 deletions tests/CompilerTest/CompilerTest.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
<Content Include="_TestData\FolderFileListGenerator\Foo.txt" />
<Content Include="_TestData\FolderFileListGenerator\Nested\Bar.txt" />
<Content Include="_TestData\IgnoreWhenFileExists\Foo.txt" />
<Content Include="_TestData\SectorDataFile\ArcGenTest.txt" />
</ItemGroup>

<Target Name="PostBuild" AfterTargets="PostBuildEvent">
Expand Down
11 changes: 11 additions & 0 deletions tests/CompilerTest/Input/EseSectorDataReaderTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,22 @@ public void ItDetectsABlankLine(string line, bool expected)
[InlineData("", false)]
[InlineData("// comment", false)]
[InlineData("/* comment */", false)]
[InlineData("@ARC", false)]
public void ItDetectsACommentLine(string line, bool expected)
{
Assert.Equal(expected, this.reader.IsCommentLine(line));
}

[Fact]
public void ItIgnoresShortComments() {
Assert.False(this.reader.IsArcGenLine("@AR"));
}

[Fact]
public void ItRecognisesArcGenLines() {
Assert.True(this.reader.IsArcGenLine("@ARC(xxx)"));
}

[Theory]
[InlineData("abc ;", "")]
[InlineData("abc ;comment", "comment")]
Expand Down
14 changes: 12 additions & 2 deletions tests/CompilerTest/Input/SctSectorDataReaderTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,21 @@ public void ItDetectsABlankLine(string line, bool expected)
[InlineData("", false)]
[InlineData("// comment", false)]
[InlineData("/* comment */", false)]
public void ItDetectsACommentLine(string line, bool expected)
{
[InlineData("@ARC", false)]
public void ItDetectsACommentLine(string line, bool expected) {
Assert.Equal(expected, this.reader.IsCommentLine(line));
}

[Fact]
public void ItIgnoresShortComments() {
Assert.False(this.reader.IsArcGenLine("@AR"));
}

[Fact]
public void ItRecognisesArcGenLines() {
Assert.True(this.reader.IsArcGenLine("@ARC(xxx)"));
}

[Theory]
[InlineData("abc ;", "")]
[InlineData("abc ;comment", "comment")]
Expand Down
26 changes: 25 additions & 1 deletion tests/CompilerTest/Input/SectorDataFileTest.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using Compiler.Input;
using Compiler.Model;
using Xunit;
Expand All @@ -8,6 +9,7 @@ namespace CompilerTest.Input
public class SectorDataFileTest
{
private readonly SectorDataFile file;
private readonly SectorDataFile arcGenFile;

public SectorDataFileTest()
{
Expand All @@ -17,6 +19,13 @@ public SectorDataFileTest()
InputDataType.ESE_AGREEMENTS,
new EseSectorDataReader()
);

arcGenFile = new SectorDataFile(
"_TestData/SectorDataFile/ArcGenTest.txt",
new InputFileStreamFactory(),
InputDataType.ESE_AGREEMENTS,
new EseSectorDataReader()
);
}

[Fact]
Expand Down Expand Up @@ -85,6 +94,21 @@ public void TestItIteratesTheInputFile()

Assert.Equal(33, file.CurrentLineNumber);
}

[Fact]
public void TestItIteratesArcGen() {
string[] lines = { "Test Region N051.31.15.000 W000.07.30.000 N051.31.14.715 W000.07.19.500", "Test Region N051.31.14.715 W000.07.19.500 N051.31.13.861 W000.07.09.079", "Test Region N051.31.13.861 W000.07.09.079 N051.31.12.444 W000.06.58.818", "Test Region N051.31.12.444 W000.06.58.818 N051.31.10.477 W000.06.48.794", "Test Region N051.31.10.477 W000.06.48.794 N051.31.07.973 W000.06.39.083", "Test Region N051.31.07.973 W000.06.39.083 N051.31.04.952 W000.06.29.760", "Test Region N051.31.04.952 W000.06.29.760 N051.31.01.436 W000.06.20.896", "Test Region N051.31.01.436 W000.06.20.896 N051.30.57.453 W000.06.12.558", "Test Region N051.30.57.453 W000.06.12.558 N051.30.53.033 W000.06.04.808", "Test Region N051.30.53.033 W000.06.04.808 N051.30.48.209 W000.05.57.708", "Test Region N051.30.48.209 W000.05.57.708 N051.30.43.018 W000.05.51.309", "Test Region N051.30.43.018 W000.05.51.309 N051.30.37.500 W000.05.45.662", "Test Region N051.30.37.500 W000.05.45.662 N051.30.31.696 W000.05.40.809", "Test Region N051.30.31.696 W000.05.40.809 N051.30.25.652 W000.05.36.787", "Test Region N051.30.25.652 W000.05.36.787 N051.30.19.411 W000.05.33.626", "Test Region N051.30.19.411 W000.05.33.626 N051.30.13.024 W000.05.31.351", "Test Region N051.30.13.024 W000.05.31.351 N051.30.06.537 W000.05.29.979", "Test Region N051.30.06.537 W000.05.29.979 N051.30.00.000 W000.05.29.521", "Test Region N051.30.00.000 W000.05.29.521 N051.29.53.463 W000.05.29.979", "Test Region N051.29.53.463 W000.05.29.979 N051.29.46.976 W000.05.31.351", "Test Region N051.29.46.976 W000.05.31.351 N051.29.40.589 W000.05.33.626", "Test Region N051.29.40.589 W000.05.33.626 N051.29.34.348 W000.05.36.787", "Test Region N051.29.34.348 W000.05.36.787 N051.29.28.304 W000.05.40.809", "Test Region N051.29.28.304 W000.05.40.809 N051.29.22.500 W000.05.45.662", "Test Region N051.29.22.500 W000.05.45.662 N051.29.16.982 W000.05.51.309", "Test Region N051.29.16.982 W000.05.51.309 N051.29.11.791 W000.05.57.708", "Test Region N051.29.11.791 W000.05.57.708 N051.29.06.967 W000.06.04.808", "Test Region N051.29.06.967 W000.06.04.808 N051.29.02.547 W000.06.12.558", "Test Region N051.29.02.547 W000.06.12.558 N051.28.58.564 W000.06.20.896", "Test Region N051.28.58.564 W000.06.20.896 N051.28.55.048 W000.06.29.760", "Test Region N051.28.55.048 W000.06.29.760 N051.28.52.027 W000.06.39.083", "Test Region N051.28.52.027 W000.06.39.083 N051.28.49.523 W000.06.48.794", "Test Region N051.28.49.523 W000.06.48.794 N051.28.47.556 W000.06.58.818", "Test Region N051.28.47.556 W000.06.58.818 N051.28.46.139 W000.07.09.079", "Test Region N051.28.46.139 W000.07.09.079 N051.28.45.285 W000.07.19.500", "Test Region N051.28.45.285 W000.07.19.500 N051.28.44.999 W000.07.30.000", "Test Region N051.28.44.999 W000.07.30.000 N051.28.45.285 W000.07.40.500", "Test Region N051.28.45.285 W000.07.40.500 N051.28.46.139 W000.07.50.921", "Test Region N051.28.46.139 W000.07.50.921 N051.28.47.556 W000.08.01.182", "Test Region N051.28.47.556 W000.08.01.182 N051.28.49.523 W000.08.11.206", "Test Region N051.28.49.523 W000.08.11.206 N051.28.52.027 W000.08.20.917", "Test Region N051.28.52.027 W000.08.20.917 N051.28.55.048 W000.08.30.240", "Test Region N051.28.55.048 W000.08.30.240 N051.28.58.564 W000.08.39.104", "Test Region N051.28.58.564 W000.08.39.104 N051.29.02.547 W000.08.47.442", "Test Region N051.29.02.547 W000.08.47.442 N051.29.06.967 W000.08.55.192", "Test Region N051.29.06.967 W000.08.55.192 N051.29.11.791 W000.09.02.292", "Test Region N051.29.11.791 W000.09.02.292 N051.29.16.982 W000.09.08.691", "Test Region N051.29.16.982 W000.09.08.691 N051.29.22.500 W000.09.14.338", "Test Region N051.29.22.500 W000.09.14.338 N051.29.28.304 W000.09.19.191", "Test Region N051.29.28.304 W000.09.19.191 N051.29.34.348 W000.09.23.213", "Test Region N051.29.34.348 W000.09.23.213 N051.29.40.589 W000.09.26.374", "Test Region N051.29.40.589 W000.09.26.374 N051.29.46.976 W000.09.28.649", "Test Region N051.29.46.976 W000.09.28.649 N051.29.53.463 W000.09.30.021", "Test Region N051.29.53.463 W000.09.30.021 N051.30.00.000 W000.09.30.479", "Test Region N051.30.00.000 W000.09.30.479 N051.30.06.537 W000.09.30.021", "Test Region N051.30.06.537 W000.09.30.021 N051.30.13.024 W000.09.28.649", "Test Region N051.30.13.024 W000.09.28.649 N051.30.19.411 W000.09.26.374", "Test Region N051.30.19.411 W000.09.26.374 N051.30.25.652 W000.09.23.213", "Test Region N051.30.25.652 W000.09.23.213 N051.30.31.696 W000.09.19.191", "Test Region N051.30.31.696 W000.09.19.191 N051.30.37.500 W000.09.14.338", "Test Region N051.30.37.500 W000.09.14.338 N051.30.43.018 W000.09.08.691", "Test Region N051.30.43.018 W000.09.08.691 N051.30.48.209 W000.09.02.292", "Test Region N051.30.48.209 W000.09.02.292 N051.30.53.033 W000.08.55.192", "Test Region N051.30.53.033 W000.08.55.192 N051.30.57.453 W000.08.47.442", "Test Region N051.30.57.453 W000.08.47.442 N051.31.01.436 W000.08.39.104", "Test Region N051.31.01.436 W000.08.39.104 N051.31.04.952 W000.08.30.240", "Test Region N051.31.04.952 W000.08.30.240 N051.31.07.973 W000.08.20.917", "Test Region N051.31.07.973 W000.08.20.917 N051.31.10.477 W000.08.11.206", "Test Region N051.31.10.477 W000.08.11.206 N051.31.12.444 W000.08.01.182", "Test Region N051.31.12.444 W000.08.01.182 N051.31.13.861 W000.07.50.921", "Test Region N051.31.13.861 W000.07.50.921 N051.31.14.715 W000.07.40.500", "Test Region N051.31.14.715 W000.07.40.500 N051.31.15.000 W000.07.30.000" };
string[] lines2 = { "Test Region N051.29.17.000 W000.06.34.000 N051.29.16.696 W000.06.34.039", "Test Region N051.29.16.696 W000.06.34.039 N051.29.13.869 W000.06.33.999", "Test Region N051.29.13.869 W000.06.33.999 N051.29.11.051 W000.06.34.355", "Test Region N051.29.11.051 W000.06.34.355 N051.29.08.264 W000.06.35.104", "Test Region N051.29.08.264 W000.06.35.104 N051.29.05.527 W000.06.36.241", "Test Region N051.29.05.527 W000.06.36.241 N051.29.02.863 W000.06.37.756", "Test Region N051.29.02.863 W000.06.37.756 N051.29.00.291 W000.06.39.639", "Test Region N051.29.00.291 W000.06.39.639 N051.28.57.831 W000.06.41.874", "Test Region N051.28.57.831 W000.06.41.874 N051.28.55.501 W000.06.44.445", "Test Region N051.28.55.501 W000.06.44.445 N051.28.52.000 W000.06.49.000" };
int i = 0;
foreach (SectorData dataLine in arcGenFile) {
if (i < lines.Length) {
Assert.Equal(lines[i], dataLine.rawData);
} else {
Assert.Equal(lines2[i - lines.Length], dataLine.rawData);
}
i += 1;
}
}

[Fact]
public void ItsEqualIfPathTheSame()
Expand Down
Loading

0 comments on commit c01cfce

Please sign in to comment.