-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added a file with coordinates in Sweden to be used for regression tes…
…ting, and implemented a test method which verifies the transformations for this current implementation. The current implementation use the three classes RT90Position, SWEREF99Position, WGS84Position, from within a new class Transformer with implementation that selects which of these classes to use based on the EPSG code for the source and target coordinates.
- Loading branch information
1 parent
6ace16e
commit 135e543
Showing
4 changed files
with
243 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
using MightyLittleGeodesy.Positions; | ||
using System; | ||
using static MightyLittleGeodesy.Positions.RT90Position; | ||
using static MightyLittleGeodesy.Positions.SWEREF99Position; | ||
|
||
namespace MightyLittleGeodesy | ||
{ | ||
|
||
public class Transformer { | ||
public const int epsgForWgs84 = 4326; | ||
private const int epsgForSweref99tm = 3006; | ||
|
||
//private const int epsgLowerValueForSwerefLocal = 3007; // the NATIONAL sweref99TM has value 3006 as in the above constant | ||
//private const int epsgUpperValueForSwerefLocal = 3018; | ||
private const int epsgLowerValueForSweref = epsgForSweref99tm; | ||
private const int epsgUpperValueForSweref = 3018; | ||
|
||
private const int epsgLowerValueForRT90 = 3019; | ||
private const int epsgUpperValueForRT90 = 3024; | ||
|
||
public static Coordinate Transform(Coordinate sourceCoordinate, int targetEpsg) { | ||
if(sourceCoordinate.epsgNumber == targetEpsg) throw new ArgumentException("Trying to transform from/to the same CRS"); | ||
if(isWgs84(sourceCoordinate.epsgNumber)) { | ||
var wgs84position = new WGS84Position(sourceCoordinate.yLatitude, sourceCoordinate.xLongitude); | ||
if(isSweref(targetEpsg)) { | ||
SWEREFProjection swerefProjection = ProjectionFactory.GetSwerefProjectionByEpsgNumber(targetEpsg); | ||
var position = new SWEREF99Position(wgs84position, swerefProjection); | ||
return new Coordinate(targetEpsg, position.Longitude, position.Latitude); | ||
} | ||
else if(isRT90(targetEpsg)) { | ||
RT90Projection rt90Projection = ProjectionFactory.GetRT90ProjectionProjectionByEpsgNumber(targetEpsg); | ||
var position = new RT90Position(wgs84position, rt90Projection); | ||
return new Coordinate(targetEpsg, position.Longitude, position.Latitude); | ||
} | ||
} | ||
else if(isSweref(sourceCoordinate.epsgNumber)) { | ||
if(isWgs84(targetEpsg)) { | ||
SWEREFProjection swerefProjection = ProjectionFactory.GetSwerefProjectionByEpsgNumber(sourceCoordinate.epsgNumber); | ||
var sweref99Position = new SWEREF99Position(sourceCoordinate.yLatitude, sourceCoordinate.xLongitude, swerefProjection); | ||
var wgs84result = sweref99Position.ToWGS84(); | ||
return new Coordinate(targetEpsg, wgs84result.Longitude, wgs84result.Latitude); | ||
} | ||
else { | ||
// the only direct transform supported is to/from WGS84, so therefore first transform to wgs84 | ||
var wgs84coordinate = Transform(sourceCoordinate, epsgForWgs84); | ||
return Transform(wgs84coordinate, targetEpsg); | ||
} | ||
} | ||
else if(isRT90(sourceCoordinate.epsgNumber)) { | ||
if(isWgs84(targetEpsg)) { | ||
RT90Projection rt90Projection = ProjectionFactory.GetRT90ProjectionProjectionByEpsgNumber(sourceCoordinate.epsgNumber); | ||
var rt90Position = new RT90Position(sourceCoordinate.yLatitude, sourceCoordinate.xLongitude, rt90Projection); | ||
var wgs84result = rt90Position.ToWGS84(); | ||
return new Coordinate(targetEpsg, wgs84result.Longitude, wgs84result.Latitude); | ||
} | ||
else { | ||
// the only direct transform supported is to/from WGS84, so therefore first transform to wgs84 | ||
var wgs84coordinate = Transform(sourceCoordinate, epsgForWgs84); | ||
return Transform(wgs84coordinate, targetEpsg); | ||
} | ||
} | ||
throw new ArgumentException(string.Format("Unhandled source/target EPSG {0} ==> {1}", sourceCoordinate.epsgNumber, targetEpsg)); | ||
} | ||
|
||
private static bool isWgs84(int epsgNumber) { | ||
return epsgNumber == epsgForWgs84; | ||
} | ||
private static bool isSweref(int epsgNumber) { | ||
return epsgLowerValueForSweref <= epsgNumber && epsgNumber <= epsgUpperValueForSweref; | ||
} | ||
private static bool isRT90(int epsgNumber) { | ||
return epsgLowerValueForRT90 <= epsgNumber && epsgNumber <= epsgUpperValueForRT90; | ||
} | ||
} | ||
|
||
public class Coordinate { | ||
public readonly int epsgNumber; | ||
public readonly double xLongitude; | ||
public readonly double yLatitude; | ||
public Coordinate( | ||
int epsgNumber, | ||
double xLongitude, | ||
double yLatitude | ||
) { | ||
this.epsgNumber = epsgNumber; | ||
this.xLongitude = xLongitude; | ||
this.yLatitude = yLatitude; | ||
} | ||
} | ||
} |
129 changes: 129 additions & 0 deletions
129
MightyLittleGeodesyTests/CoordinateFiles/TransformingCoordinatesFromFileTest.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
using System; | ||
using System.Linq; | ||
using System.Collections.Generic; | ||
using Microsoft.VisualStudio.TestTools.UnitTesting; | ||
using System.IO; | ||
using MightyLittleGeodesy; | ||
|
||
namespace SwedenCrsTransformationsTests.CoordinateFiles { | ||
|
||
[TestClass] | ||
public class TransformingCoordinatesFromFileTest { | ||
|
||
internal const string columnSeparator = "|"; | ||
|
||
// the below file "swedish_crs_transformations.csv" was copied from: https://github.com/TomasJohansson/crsTransformations/blob/a1da6c74daf040a521beb32f9f395124ffe76aa6/crs-transformation-adapter-test/src/test/resources/generated/swedish_crs_coordinates.csv | ||
// and it was generated with a method "createFileWithTransformationResultsForCoordinatesInSweden()" at https://github.com/TomasJohansson/crsTransformations/blob/a1da6c74daf040a521beb32f9f395124ffe76aa6/crs-transformation-adapter-test/src/test/java/com/programmerare/com/programmerare/testData/CoordinateTestDataGeneratedFromEpsgDatabaseTest.java | ||
private const string relativePathForFileWith_swedish_crs_transformations = "CoordinateFiles/data/swedish_crs_coordinates.csv"; | ||
// the project file should use "CopyToOutputDirectory" for the above file | ||
|
||
|
||
[TestMethod] | ||
public void AssertThatTransformationsDoNotDifferTooMuchFromExpectedResultInFile() { | ||
string directory = GetPathToOutputDirectoryWhereTheDataFileShouldBeCopiedToAutomatically(); | ||
string absolutePathToFile = Path.Combine(directory, relativePathForFileWith_swedish_crs_transformations).Replace('/', Path.DirectorySeparatorChar); | ||
FileInfo file = new FileInfo(absolutePathToFile); | ||
Assert.IsTrue(file.Exists, "Try the build action 'CopyToOutputDirectory' ! . The file could not be found: " + file.FullName); | ||
|
||
IList<string> problemTransformationResults = new List<string>(); | ||
IList<string> lines = File.ReadAllLines(file.FullName); | ||
// The first two lines of the input file (the header row, and a data row): | ||
// EPSG 4326 (WGS84)Longitude for WGS84 (EPSG 4326)|Latitude for WGS84 (EPSG 4326)|EPSG 3006|X for EPSG 3006|Y for EPSG 3006|EPSG 3007-3024|X for EPSG 3007-3024|Y for EPSG 3007-3024|Implementation count for EPSG 3006 transformation|Implementation count for EPSG 3007-3024 transformation | ||
// 4326|12.146151472138385|58.46573396912418|3006|333538.2957000149|6484098.2550872|3007|158529.85136620898|6483166.205771873|6|6 | ||
// The last two columns can be ignored here, but the first nine columns are in three pairs with three columns each: | ||
// an epsg number, and then the longitude(x) and latitude(y) for that coordinate. | ||
// All three coordinates in one row represents the same location but in different coordinate reference systems. | ||
// The first two, of the three, coordinates are for the same coordinate reference systems, WGS84 and SWEREF99TM, | ||
// but the third is different for all rows (18 data rows for the local swedish CRS systems, RT90 and SWEREF99, with EPSG codes 3007-3024). | ||
|
||
// The below loop iterates all lines and makes transformations between (to and from) the three coordinate reference systems | ||
// and verifies the expected result according to the file, and asserts with an error if the difference is too big. | ||
// Note that the expected coordinates have been calculated in another project, by using a median value for 6 different implementations. | ||
// (and the number 6 is actually what the last columns means i.e. how many implementations were used to create the data file) | ||
IList<Coordinates> listOfCoordinates = lines.Select(line => new Coordinates(line)).Skip(1).ToList(); | ||
Assert.AreEqual(18, listOfCoordinates.Count); | ||
int numberOfTransformations = 0; | ||
foreach(var listOfCoordinatesWhichRepresentTheSameLocation in listOfCoordinates) { | ||
IList<Coordinate> coordinates = listOfCoordinatesWhichRepresentTheSameLocation.coordinateList; | ||
for(int i=0; i<coordinates.Count-1; i++) { | ||
for(int j=i+1; j<coordinates.Count; j++) { | ||
Transform(coordinates[i], coordinates[j], problemTransformationResults); | ||
Transform(coordinates[j], coordinates[i], problemTransformationResults); | ||
numberOfTransformations += 2; | ||
} | ||
} | ||
|
||
} | ||
if (problemTransformationResults.Count > 0) { | ||
foreach (string s in problemTransformationResults) { | ||
Console.WriteLine(s); | ||
} | ||
} | ||
Assert.AreEqual(0, problemTransformationResults.Count, "For further details see the Console output"); | ||
|
||
const int expectedNumberOfTransformations = 108; // for an explanation, see the lines below: | ||
// Each line in the input file "swedish_crs_coordinates.csv" has three coordinates (and let's below call then A B C) | ||
// and then for each line we should have done six number of transformations: | ||
// A ==> B | ||
// A ==> C | ||
// B ==> C | ||
// (and three more in the opposite directions) | ||
// And there are 18 local CRS for sweden (i.e number of data rows in the file) | ||
// Thus the total number of transformations should be 18 * 6 = 108 | ||
Assert.AreEqual(expectedNumberOfTransformations, numberOfTransformations); | ||
} | ||
|
||
|
||
|
||
private void Transform( | ||
Coordinate sourceCoordinate, | ||
Coordinate targetCoordinateExpected, | ||
IList<string> problemTransformationResults | ||
) { | ||
int targetEpsg = targetCoordinateExpected.epsgNumber; | ||
Coordinate targetCoordinate = Transformer.Transform(sourceCoordinate, targetEpsg); | ||
bool isTargetEpsgWgs84 = targetEpsg == Transformer.epsgForWgs84; | ||
// double maxDifference = isTargetEpsgWgs84 ? 0.000002 : 0.2; // fails, Epsg 3022 ==> 4326 , diffLongitude 2.39811809521484E-06 | ||
// double maxDifference = isTargetEpsgWgs84 ? 000003 : 0.1; // fails, Epsg 4326 ==> 3022 , diffLongitude 0.117090131156147 | ||
double maxDifference = isTargetEpsgWgs84 ? 000003 : 0.2; // the other (i.e. non-WGS84) are using meter as unit, so 0.2 is just two decimeters difference | ||
double diffLongitude = Math.Abs((targetCoordinate.xLongitude - targetCoordinateExpected.xLongitude)); | ||
double diffLatitude = Math.Abs((targetCoordinate.yLatitude - targetCoordinateExpected.yLatitude)); | ||
|
||
if (diffLongitude > maxDifference || diffLatitude > maxDifference) { | ||
string problem = string.Format( | ||
"Epsg {0} ==> {1} , diffLongitude {2} , diffLatitude {3}" | ||
+ "sourceCoordinate xLongitude/yLatitude: {4}/{5}" | ||
+ "targetCoordinate xLongitude/yLatitude: {6}/{7}" | ||
+ "targetCoordinateExpected xLongitude/yLatitude: {8}/{9}", | ||
sourceCoordinate.epsgNumber, targetCoordinateExpected.epsgNumber, | ||
diffLongitude, diffLatitude, | ||
sourceCoordinate.xLongitude, sourceCoordinate.yLatitude, | ||
targetCoordinate.xLongitude, targetCoordinate.yLatitude, | ||
targetCoordinateExpected.xLongitude, targetCoordinateExpected.yLatitude | ||
); | ||
problemTransformationResults.Add(problem); | ||
} | ||
} | ||
|
||
private string GetPathToOutputDirectoryWhereTheDataFileShouldBeCopiedToAutomatically() { | ||
string pathToOutputDirectory= Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location); | ||
// e.g. a path ending with something like this: "... SwedenCrsTransformationsTests\bin\Debug\netcoreapp2.1" | ||
return pathToOutputDirectory; | ||
} | ||
} | ||
|
||
internal class Coordinates { | ||
internal readonly List<Coordinate> coordinateList; | ||
internal Coordinates( | ||
string lineFromFile | ||
) { | ||
var array = lineFromFile.Split(TransformingCoordinatesFromFileTest.columnSeparator); | ||
coordinateList = new List<Coordinate> { | ||
new Coordinate(int.Parse(array[0]), double.Parse(array[1]), double.Parse(array[2])), | ||
new Coordinate(int.Parse(array[3]), double.Parse(array[4]), double.Parse(array[5])), | ||
new Coordinate(int.Parse(array[6]), double.Parse(array[7]), double.Parse(array[8])) | ||
}; | ||
} | ||
} | ||
|
||
} |
19 changes: 19 additions & 0 deletions
19
MightyLittleGeodesyTests/CoordinateFiles/data/swedish_crs_coordinates.csv
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
EPSG 4326 (WGS84)Longitude for WGS84 (EPSG 4326)|Latitude for WGS84 (EPSG 4326)|EPSG 3006|X for EPSG 3006|Y for EPSG 3006|EPSG 3007-3024|X for EPSG 3007-3024|Y for EPSG 3007-3024|Implementation count for EPSG 3006 transformation|Implementation count for EPSG 3007-3024 transformation | ||
4326|12.146151472138385|58.46573396912418|3006|333538.2957000149|6484098.2550872|3007|158529.85136620898|6483166.205771873|6|6 | ||
4326|13.470524334397624|58.58400993288184|3006|411075.37423013494|6494745.783843142|3008|148285.48862749408|6496331.697852695|6|6 | ||
4326|14.977576047737374|58.78282472189397|3006|498703.5890065983|6515870.052578431|3009|148703.07023469123|6518477.443555852|6|6 | ||
4326|16.511041403075566|59.30321531687106|3006|586043.5750084487|6574791.715626789|3010|150629.0196083946|6576446.640618691|6|6 | ||
4326|18.19079263233461|59.678160851467794|3006|679655.2692001299|6619889.429962698|3011|160749.4537030397|6618232.315005776|6|6 | ||
4326|13.721704597205232|62.96891741227423|3006|435185.3935222791|6982770.168583414|3012|123201.54397365177|6985030.111975779|6|6 | ||
4326|15.5453884066594|62.99805848320907|3006|527626.9057630751|6985490.052601999|3013|139631.07711081076|6988184.660387672|6|6 | ||
4326|16.829108886010665|64.99460630590642|3006|586262.6436480937|7209101.47066392|3014|130140.34937436534|7210803.867463439|6|6 | ||
4326|18.76546663731605|63.52666974241647|3006|687205.0085568134|7049780.682800813|3015|150769.58533200162|7047090.338527749|6|6 | ||
4326|19.95189549865619|66.86485452747677|3006|716925.6906953243|7424941.873508802|3016|136924.61238266266|7419313.586609598|6|6 | ||
4326|21.388317251528505|65.72336003627437|3006|792678.6631042717|7303969.768206225|3017|133400.26969797508|7292040.086681188|6|6 | ||
4326|23.01759451041915|66.743305441312|3006|852474.0312307824|7425471.195805718|3018|139755.73621974868|7405746.358696348|6|6 | ||
4326|12.000334935356666|58.49748841971714|3006|325194.2619877269|6488002.126540723|3019|1540497.4440695136|6486274.58960603|6|6 | ||
4326|13.535611821690898|60.061656774989856|3006|418476.30827460607|6659180.662580081|3020|1498894.371510969|6660313.85211511|6|6 | ||
4326|15.816797928880426|61.291598649254105|3006|543770.853060645|6795541.286404101|3021|1500627.951348714|6797357.300170688|6|6 | ||
4326|18.073441702621203|64.15010420890953|3006|649477.2997972438|7117350.010634856|3022|1500917.9130901312|7115957.205396191|6|6 | ||
4326|20.543455199211188|66.20776695378555|3006|749287.0042619368|7354114.654538377|3023|1510782.4273971773|7345394.689191865|6|6 | ||
4326|22.795306156014576|66.83989741757875|3006|841397.9017335944|7434928.316155667|3024|1510614.3821736928|7415882.894158065|6|6 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters