diff --git a/src/ProjNet/CoordinateSystems/Projection.cs b/src/ProjNet/CoordinateSystems/Projection.cs index 1372986..a1be243 100644 --- a/src/ProjNet/CoordinateSystems/Projection.cs +++ b/src/ProjNet/CoordinateSystems/Projection.cs @@ -76,6 +76,17 @@ public ProjectionParameter GetParameter(int index) return _parameters[index]; } + /// + /// Adds a parameter to the parameter list + /// + /// The projection parameter name + /// The value + internal void AddParameter(string name, double value) + { + var param = new ProjectionParameter(name, value); + _parameters.Add(param); + } + /// /// Gets an named parameter of the projection. /// diff --git a/src/ProjNet/CoordinateSystems/Projections/MapProjection.cs b/src/ProjNet/CoordinateSystems/Projections/MapProjection.cs index a369567..0d1ac44 100644 --- a/src/ProjNet/CoordinateSystems/Projections/MapProjection.cs +++ b/src/ProjNet/CoordinateSystems/Projections/MapProjection.cs @@ -244,6 +244,14 @@ public ProjectionParameter GetParameter(string name) return _Parameters.Find(name); } + /// + /// Adds a parameter to the parameter list + /// + internal void AddParameter(string name, double value) + { + _Parameters.Add(name, value); + } + /// /// /// diff --git a/src/ProjNet/IO/CoordinateSystems/CoordinateSystemWktReader.cs b/src/ProjNet/IO/CoordinateSystems/CoordinateSystemWktReader.cs index a5b3dae..002f049 100644 --- a/src/ProjNet/IO/CoordinateSystems/CoordinateSystemWktReader.cs +++ b/src/ProjNet/IO/CoordinateSystems/CoordinateSystemWktReader.cs @@ -109,7 +109,7 @@ private static IUnit ReadUnit(WktStreamTokenizer tokenizer) tokenizer.ReadAuthority(out authority, out authorityCode); tokenizer.ReadCloser(bracket); } - else + else tokenizer.CheckCloser(bracket); return new Unit(unitsPerUnit, unitName, authority, authorityCode, string.Empty, string.Empty, string.Empty); @@ -192,6 +192,7 @@ private static AxisInfo ReadAxis(WktStreamTokenizer tokenizer) case "SOUTH": return new AxisInfo(axisName, AxisOrientationEnum.South); case "UP": return new AxisInfo(axisName, AxisOrientationEnum.Up); case "WEST": return new AxisInfo(axisName, AxisOrientationEnum.West); + case "UNKNOWN": return new AxisInfo(axisName, AxisOrientationEnum.Other); default: throw new ArgumentException("Invalid axis name '" + unitname + "' in WKT"); } @@ -206,7 +207,7 @@ private static CoordinateSystem ReadCoordinateSystem(string coordinateSystem, Wk case "PROJCS": return ReadProjectedCoordinateSystem(tokenizer); case "FITTED_CS": - return ReadFittedCoordinateSystem (tokenizer); + return ReadFittedCoordinateSystem(tokenizer); case "GEOCCS": return ReadGeocentricCoordinateSystem(tokenizer); case "COMPD_CS": @@ -285,7 +286,7 @@ private static Ellipsoid ReadEllipsoid(WktStreamTokenizer tokenizer) return ellipsoid; } - private static IProjection ReadProjection(WktStreamTokenizer tokenizer) + private static Projection ReadProjection(WktStreamTokenizer tokenizer) { if (tokenizer.GetStringValue() != "PROJECTION") tokenizer.ReadToken("PROJECTION"); @@ -303,34 +304,46 @@ private static IProjection ReadProjection(WktStreamTokenizer tokenizer) else tokenizer.CheckCloser(bracket); - tokenizer.ReadToken(",");//, - tokenizer.ReadToken("PARAMETER"); var paramList = new List(); - while (tokenizer.GetStringValue() == "PARAMETER") - { - bracket = tokenizer.ReadOpener(); - string paramName = tokenizer.ReadDoubleQuotedWord(); - tokenizer.ReadToken(","); - tokenizer.NextToken(); - double paramValue = tokenizer.GetNumericValue(); - tokenizer.ReadCloser(bracket); - paramList.Add(new ProjectionParameter(paramName, paramValue)); - //tokenizer.ReadToken(","); - //tokenizer.NextToken(); - tokenizer.NextToken(); - if (tokenizer.GetStringValue() == ",") - { - tokenizer.NextToken(); - } - else - { - break; - } - } + var projection = new Projection(projectionName, paramList, projectionName, authority, authorityCode, string.Empty, string.Empty, string.Empty); return projection; } + private static void ReadParamater(WktStreamTokenizer tokenizer, Projection projection) + { + var bracket = tokenizer.ReadOpener(); + string paramName = tokenizer.ReadDoubleQuotedWord(); + tokenizer.ReadToken(","); + tokenizer.NextToken(); + double paramValue = tokenizer.GetNumericValue(); + tokenizer.ReadCloser(bracket); + + projection.AddParameter(paramName, paramValue); + } + + private static void ReadExtension(WktStreamTokenizer tokenizer) + { + var bracket = tokenizer.ReadOpener(); + string paramName = tokenizer.ReadDoubleQuotedWord(); + tokenizer.ReadToken(","); + tokenizer.NextToken(); + if (paramName == "PROJ4") + { + string paramValue = tokenizer.ReadDoubleQuotedWord(); + } + else + { + System.Diagnostics.Debug.WriteLine("What are you?"); + } + + tokenizer.ReadCloser(bracket); + + System.Diagnostics.Debug.WriteLine("Now what do we do with you?"); + } + + + private static ProjectedCoordinateSystem ReadProjectedCoordinateSystem(WktStreamTokenizer tokenizer) { /*PROJCS[ @@ -356,42 +369,64 @@ private static ProjectedCoordinateSystem ReadProjectedCoordinateSystem(WktStream */ var bracket = tokenizer.ReadOpener(); string name = tokenizer.ReadDoubleQuotedWord(); - tokenizer.ReadToken(","); - tokenizer.ReadToken("GEOGCS"); - var geographicCS = ReadGeographicCoordinateSystem(tokenizer); - tokenizer.ReadToken(","); - tokenizer.ReadToken("PROJECTION"); - var projection = ReadProjection(tokenizer); - var unit = ReadLinearUnit(tokenizer); + tokenizer.NextToken(); + + GeographicCoordinateSystem geographicCS = null; + Projection projection = null; + LinearUnit unit = null; var axisInfo = new List(2); string authority = string.Empty; long authorityCode = -1; - tokenizer.NextToken(); - if (tokenizer.GetStringValue() == ",") + while (tokenizer.GetStringValue() == ",") { tokenizer.NextToken(); - while (tokenizer.GetStringValue() == "AXIS") - { - axisInfo.Add(ReadAxis(tokenizer)); - tokenizer.NextToken(); - if (tokenizer.GetStringValue() == ",") tokenizer.NextToken(); - } - if (tokenizer.GetStringValue() == ",") tokenizer.NextToken(); - if (tokenizer.GetStringValue() == "AUTHORITY") + string nextToken = tokenizer.GetStringValue(); + switch (nextToken) { - tokenizer.ReadAuthority(out authority, out authorityCode); - tokenizer.ReadCloser(bracket); + case "AUTHORITY": + tokenizer.ReadAuthority(out authority, out authorityCode); + break; + case "AXIS": + axisInfo.Add(ReadAxis(tokenizer)); + break; + case "GEOGCS": + geographicCS = ReadGeographicCoordinateSystem(tokenizer); + break; + case "PARAMETER": + ReadParamater(tokenizer, projection); + break; + case "PROJECTION": + projection = ReadProjection(tokenizer); + break; + case "UNIT": + unit = ReadLinearUnit(tokenizer); + break; + case "EXTENSION": + ReadExtension(tokenizer); + break; + default: + throw new ArgumentException("Token not found for {0]", nextToken); } + + tokenizer.NextToken(); } - //This is default axis values if not specified. + + // we should be at the end already!? + tokenizer.CheckCloser(bracket); + if (axisInfo.Count == 0) { axisInfo.Add(new AxisInfo("X", AxisOrientationEnum.East)); axisInfo.Add(new AxisInfo("Y", AxisOrientationEnum.North)); } - var projectedCS = new ProjectedCoordinateSystem(geographicCS.HorizontalDatum, geographicCS, unit as LinearUnit, projection, axisInfo, name, authority, authorityCode, string.Empty, string.Empty, string.Empty); - return projectedCS; + + if (geographicCS != null) + { + return new ProjectedCoordinateSystem(geographicCS.HorizontalDatum, geographicCS, unit as LinearUnit, projection, axisInfo, name, authority, authorityCode, string.Empty, string.Empty, string.Empty); + } + + return null; } private static VerticalCoordinateSystem ReadVerticalCoordinateSystem(WktStreamTokenizer tokenizer) @@ -451,10 +486,10 @@ private static CompoundCoordinateSystem ReadCompoundCoordinateSystem(WktStreamTo long authorityCode = -1; tokenizer.NextToken(); - if ( tokenizer.GetStringValue() == ",") + if (tokenizer.GetStringValue() == ",") { tokenizer.NextToken(); - if(tokenizer.GetStringValue() == "AUTHORITY") + if (tokenizer.GetStringValue() == "AUTHORITY") { tokenizer.ReadAuthority(out authority, out authorityCode); } @@ -541,23 +576,27 @@ private static GeographicCoordinateSystem ReadGeographicCoordinateSystem(WktStre long authorityCode = -1; tokenizer.NextToken(); var info = new List(2); - if (tokenizer.GetStringValue() == ",") + while (tokenizer.GetStringValue() == ",") { tokenizer.NextToken(); - while (tokenizer.GetStringValue() == "AXIS") + string nextToken = tokenizer.GetStringValue(); + if (nextToken == "AXIS") { info.Add(ReadAxis(tokenizer)); - tokenizer.NextToken(); - if (tokenizer.GetStringValue() == ",") tokenizer.NextToken(); } - if (tokenizer.GetStringValue() == ",") tokenizer.NextToken(); - if (tokenizer.GetStringValue() == "AUTHORITY") + else if (nextToken == "AUTHORITY") { tokenizer.ReadAuthority(out authority, out authorityCode); - tokenizer.ReadCloser(bracket); } + + tokenizer.NextToken(); } + //We should be closed already + tokenizer.CheckCloser(bracket); + //if (tokenizer.GetStringValue() != "]") + // tokenizer.ReadCloser(bracket); + //This is default axis values if not specified. if (info.Count == 0) { @@ -575,6 +614,8 @@ private static HorizontalDatum ReadHorizontalDatum(WktStreamTokenizer tokenizer) Wgs84ConversionInfo wgsInfo = null; string authority = string.Empty; long authorityCode = -1; + string extension = string.Empty; + string extensionMethod = string.Empty; var bracket = tokenizer.ReadOpener(); string name = tokenizer.ReadDoubleQuotedWord(); @@ -595,6 +636,11 @@ private static HorizontalDatum ReadHorizontalDatum(WktStreamTokenizer tokenizer) tokenizer.ReadAuthority(out authority, out authorityCode); tokenizer.ReadCloser(bracket); } + else if (tokenizer.GetStringValue() == "EXTENSION") + { + tokenizer.ReadExtension(out extension, out extensionMethod); + tokenizer.ReadCloser(bracket); + } } // make an assumption about the datum type. var horizontalDatum = new HorizontalDatum(ellipsoid, wgsInfo, DatumType.HD_Geocentric, name, authority, authorityCode, string.Empty, string.Empty, string.Empty); @@ -612,7 +658,7 @@ private static VerticalDatum ReadVerticalDatum(WktStreamTokenizer tokenizer) string name = tokenizer.ReadDoubleQuotedWord(); tokenizer.ReadToken(","); tokenizer.NextToken(); - var datumType = (DatumType) tokenizer.GetNumericValue(); + var datumType = (DatumType)tokenizer.GetNumericValue(); tokenizer.NextToken(); if (tokenizer.GetStringValue() == ",") { @@ -623,7 +669,7 @@ private static VerticalDatum ReadVerticalDatum(WktStreamTokenizer tokenizer) tokenizer.ReadCloser(bracket); } } - var verticalDatum = new VerticalDatum( datumType, name, authority, authorityCode, string.Empty, string.Empty, string.Empty); + var verticalDatum = new VerticalDatum(datumType, name, authority, authorityCode, string.Empty, string.Empty, string.Empty); return verticalDatum; } @@ -654,7 +700,7 @@ private static PrimeMeridian ReadPrimeMeridian(WktStreamTokenizer tokenizer) return primeMeridian; } - private static FittedCoordinateSystem ReadFittedCoordinateSystem (WktStreamTokenizer tokenizer) + private static FittedCoordinateSystem ReadFittedCoordinateSystem(WktStreamTokenizer tokenizer) { /* FITTED_CS[ @@ -676,21 +722,21 @@ private static FittedCoordinateSystem ReadFittedCoordinateSystem (WktStreamToken ] */ var bracket = tokenizer.ReadOpener(); - string name = tokenizer.ReadDoubleQuotedWord (); - tokenizer.ReadToken (","); - tokenizer.ReadToken ("PARAM_MT"); - var toBaseTransform = MathTransformWktReader.ReadMathTransform (tokenizer); - tokenizer.ReadToken (","); - tokenizer.NextToken (); - var baseCS = ReadCoordinateSystem (null, tokenizer); + string name = tokenizer.ReadDoubleQuotedWord(); + tokenizer.ReadToken(","); + tokenizer.ReadToken("PARAM_MT"); + var toBaseTransform = MathTransformWktReader.ReadMathTransform(tokenizer); + tokenizer.ReadToken(","); + tokenizer.NextToken(); + var baseCS = ReadCoordinateSystem(null, tokenizer); string authority = string.Empty; long authorityCode = -1; - var ct = tokenizer.NextToken (); + var ct = tokenizer.NextToken(); while (ct != TokenType.Eol && ct != TokenType.Eof) { - switch (tokenizer.GetStringValue ()) + switch (tokenizer.GetStringValue()) { case ",": break; @@ -700,14 +746,14 @@ private static FittedCoordinateSystem ReadFittedCoordinateSystem (WktStreamToken break; case "AUTHORITY": - tokenizer.ReadAuthority (out authority, out authorityCode); + tokenizer.ReadAuthority(out authority, out authorityCode); //tokenizer.ReadCloser(bracket); break; } - ct = tokenizer.NextToken (); + ct = tokenizer.NextToken(); } - var fittedCS = new FittedCoordinateSystem (baseCS, toBaseTransform, name, authority, authorityCode, string.Empty, string.Empty, string.Empty); + var fittedCS = new FittedCoordinateSystem(baseCS, toBaseTransform, name, authority, authorityCode, string.Empty, string.Empty, string.Empty); return fittedCS; } } diff --git a/src/ProjNet/IO/CoordinateSystems/WKTStreamTokenizer.cs b/src/ProjNet/IO/CoordinateSystems/WKTStreamTokenizer.cs index c6fa98b..ceab90e 100644 --- a/src/ProjNet/IO/CoordinateSystems/WKTStreamTokenizer.cs +++ b/src/ProjNet/IO/CoordinateSystems/WKTStreamTokenizer.cs @@ -169,5 +169,18 @@ public void ReadAuthority(out string authority, out long authorityCode) long.TryParse(ReadDoubleQuotedWord(), NumberStyles.Any, _nfi, out authorityCode); ReadCloser(bracket); } + + public void ReadExtension(out string extension, out string extensionMethod) + { + //EXTENSION["PROJ4_GRIDS","CA61_003.gsb"] + if (GetStringValue() != "EXTENSION") + ReadToken("EXTENSION"); + var bracket = ReadOpener(); + extension = ReadDoubleQuotedWord(); + ReadToken(","); + NextToken(); + extensionMethod = ReadDoubleQuotedWord(); + ReadCloser(bracket); + } } } diff --git a/test/ProjNet.Tests/WKT/WKTParseExtensionTests.cs b/test/ProjNet.Tests/WKT/WKTParseExtensionTests.cs new file mode 100644 index 0000000..c0d35d9 --- /dev/null +++ b/test/ProjNet.Tests/WKT/WKTParseExtensionTests.cs @@ -0,0 +1,37 @@ +using Microsoft.VisualStudio.TestPlatform.Common.ExtensionFramework.Utilities; +using NUnit.Framework; +using ProjNet.CoordinateSystems; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ProjNET.Tests.WKT +{ + internal class WKTParseTests_11_17_2022 + { + private readonly CoordinateSystemFactory _coordinateSystemFactory = new CoordinateSystemFactory(); + + private const string extensionWkt1 = "PROJCS[\"NAD27/BLM59N(ftUS)\",GEOGCS[\"NAD27\",DATUM[\"North_American_Datum_1927\",SPHEROID[\"Clarke1866\",6378206.4,294.978698213898],EXTENSION[\"PROJ4_GRIDS\",\"NTv2_0.gsb\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4267\"]],PROJECTION[\"Transverse_Mercator\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",171],PARAMETER[\"scale_factor\",0.9996],PARAMETER[\"false_easting\",1640416.67],PARAMETER[\"false_northing\",0],UNIT[\"USsurveyfoot\",0.304800609601219],AXIS[\"Easting\",EAST],AXIS[\"Northing\",NORTH],AUTHORITY[\"EPSG\",\"4399\"]]"; + private const string extensionWkt2 = "PROJCS[\"NAD27/BLM60N(ftUS)\",GEOGCS[\"NAD27\",DATUM[\"North_American_Datum_1927\",SPHEROID[\"Clarke1866\",6378206.4,294.978698213898],EXTENSION[\"PROJ4_GRIDS\",\"NTv2_0.gsb\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4267\"]],PROJECTION[\"Transverse_Mercator\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",177],PARAMETER[\"scale_factor\",0.9996],PARAMETER[\"false_easting\",1640416.67],PARAMETER[\"false_northing\",0],UNIT[\"USsurveyfoot\",0.304800609601219],AXIS[\"Easting\",EAST],AXIS[\"Northing\",NORTH],AUTHORITY[\"EPSG\",\"4400\"]]"; + private const string extensionWkt3 = "PROJCS[\"WGS84/Pseudo-Mercator\",GEOGCS[\"WGS84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS84\",6378137,298.257223563,AUTHORITY[\"EPSG\",\"7030\"]],AUTHORITY[\"EPSG\",\"6326\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4326\"]],PROJECTION[\"Mercator_1SP\"],PARAMETER[\"central_meridian\",0],PARAMETER[\"scale_factor\",1],PARAMETER[\"false_easting\",0],PARAMETER[\"false_northing\",0],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],AXIS[\"Easting\",EAST],AXIS[\"Northing\",NORTH],EXTENSION[\"PROJ4\",\"+proj=merc+a=6378137+b=6378137+lat_ts=0+lon_0=0+x_0=0+y_0=0+k=1+units=m+nadgrids=@null+wktext+no_defs\"],AUTHORITY[\"EPSG\",\"3857\"]]"; + private const string extensionWkt4 = "COMPD_CS[\"WGS84/Pseudo-Mercator+EGM2008geoidheight\",PROJCS[\"WGS84/Pseudo-Mercator\",GEOGCS[\"WGS84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS84\",6378137,298.257223563,AUTHORITY[\"EPSG\",\"7030\"]],AUTHORITY[\"EPSG\",\"6326\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4326\"]],PROJECTION[\"Mercator_1SP\"],PARAMETER[\"central_meridian\",0],PARAMETER[\"scale_factor\",1],PARAMETER[\"false_easting\",0],PARAMETER[\"false_northing\",0],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],AXIS[\"Easting\",EAST],AXIS[\"Northing\",NORTH],EXTENSION[\"PROJ4\",\"+proj=merc+a=6378137+b=6378137+lat_ts=0+lon_0=0+x_0=0+y_0=0+k=1+units=m+nadgrids=@null+wktext+no_defs\"],AUTHORITY[\"EPSG\",\"3857\"]],VERT_CS[\"EGM2008height\",VERT_DATUM[\"EGM2008geoid\",2005,AUTHORITY[\"EPSG\",\"1027\"]],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],AXIS[\"Gravity-relatedheight\",UP],AUTHORITY[\"EPSG\",\"3855\"]],AUTHORITY[\"EPSG\",\"6871\"]]"; + + [Test] + public void TestExtensions() + { + CoordinateSystem cs = null; + Assert.That(() => cs = _coordinateSystemFactory.CreateFromWkt(extensionWkt1) as CoordinateSystem, Throws.Nothing); + + cs = null; + Assert.That(() => cs = _coordinateSystemFactory.CreateFromWkt(extensionWkt2) as CoordinateSystem, Throws.Nothing); + + cs = null; + Assert.That(() => cs = _coordinateSystemFactory.CreateFromWkt(extensionWkt3) as CoordinateSystem, Throws.Nothing); + + cs = null; + Assert.That(() => cs = _coordinateSystemFactory.CreateFromWkt(extensionWkt4) as CoordinateSystem, Throws.Nothing); + } + } +}