diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 088d590..193bfa1 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -38,7 +38,7 @@ 2 - 0 + 1 0 $([System.DateTime]::UtcNow.Ticks) diff --git a/src/ProjNet/IO/CoordinateSystems/CoordinateSystemWktReader.cs b/src/ProjNet/IO/CoordinateSystems/CoordinateSystemWktReader.cs index 340dcbf..7cda4f9 100644 --- a/src/ProjNet/IO/CoordinateSystems/CoordinateSystemWktReader.cs +++ b/src/ProjNet/IO/CoordinateSystems/CoordinateSystemWktReader.cs @@ -96,7 +96,7 @@ public static IInfo Parse(string wkt) /// An object that implements the IUnit interface. private static IUnit ReadUnit(WktStreamTokenizer tokenizer) { - tokenizer.ReadToken("["); + var bracket = tokenizer.ReadOpener(); string unitName = tokenizer.ReadDoubleQuotedWord(); tokenizer.ReadToken(","); tokenizer.NextToken(); @@ -107,8 +107,11 @@ private static IUnit ReadUnit(WktStreamTokenizer tokenizer) if (tokenizer.GetStringValue() == ",") { tokenizer.ReadAuthority(out authority, out authorityCode); - tokenizer.ReadToken("]"); + tokenizer.ReadCloser(bracket); } + else + tokenizer.CheckCloser(bracket); + return new Unit(unitsPerUnit, unitName, authority, authorityCode, string.Empty, string.Empty, string.Empty); } /// @@ -118,7 +121,8 @@ private static IUnit ReadUnit(WktStreamTokenizer tokenizer) /// An object that implements the IUnit interface. private static LinearUnit ReadLinearUnit(WktStreamTokenizer tokenizer) { - tokenizer.ReadToken("["); + var bracket = tokenizer.ReadOpener(); + string unitName = tokenizer.ReadDoubleQuotedWord(); tokenizer.ReadToken(","); tokenizer.NextToken(); @@ -129,8 +133,11 @@ private static LinearUnit ReadLinearUnit(WktStreamTokenizer tokenizer) if (tokenizer.GetStringValue() == ",") { tokenizer.ReadAuthority(out authority, out authorityCode); - tokenizer.ReadToken("]"); + tokenizer.ReadCloser(bracket); } + else + tokenizer.CheckCloser(bracket); + return new LinearUnit(unitsPerUnit, unitName, authority, authorityCode, string.Empty, string.Empty, string.Empty); } /// @@ -140,7 +147,8 @@ private static LinearUnit ReadLinearUnit(WktStreamTokenizer tokenizer) /// An object that implements the IUnit interface. private static AngularUnit ReadAngularUnit(WktStreamTokenizer tokenizer) { - tokenizer.ReadToken("["); + var bracket = tokenizer.ReadOpener(); + string unitName = tokenizer.ReadDoubleQuotedWord(); tokenizer.ReadToken(","); tokenizer.NextToken(); @@ -151,7 +159,11 @@ private static AngularUnit ReadAngularUnit(WktStreamTokenizer tokenizer) if (tokenizer.GetStringValue() == ",") { tokenizer.ReadAuthority(out authority, out authorityCode); - tokenizer.ReadToken("]"); + tokenizer.ReadCloser(bracket); + } + else + { + tokenizer.CheckCloser(bracket); } return new AngularUnit(unitsPerUnit, unitName, authority, authorityCode, string.Empty, string.Empty, string.Empty); } @@ -165,12 +177,12 @@ private static AxisInfo ReadAxis(WktStreamTokenizer tokenizer) { if (tokenizer.GetStringValue() != "AXIS") tokenizer.ReadToken("AXIS"); - tokenizer.ReadToken("["); + var bracket = tokenizer.ReadOpener(); string axisName = tokenizer.ReadDoubleQuotedWord(); tokenizer.ReadToken(","); tokenizer.NextToken(); string unitname = tokenizer.GetStringValue(); - tokenizer.ReadToken("]"); + tokenizer.ReadCloser(bracket); switch (unitname.ToUpperInvariant()) { case "DOWN": return new AxisInfo(axisName, AxisOrientationEnum.Down); @@ -210,7 +222,7 @@ private static CoordinateSystem ReadCoordinateSystem(string coordinateSystem, Wk private static Wgs84ConversionInfo ReadWGS84ConversionInfo(WktStreamTokenizer tokenizer) { //TOWGS84[0,0,0,0,0,0,0] - tokenizer.ReadToken("["); + var bracket = tokenizer.ReadOpener(); var info = new Wgs84ConversionInfo(); tokenizer.NextToken(); info.Dx = tokenizer.GetNumericValue(); @@ -244,14 +256,14 @@ private static Wgs84ConversionInfo ReadWGS84ConversionInfo(WktStreamTokenizer to } } if (tokenizer.GetStringValue() != "]") - tokenizer.ReadToken("]"); + tokenizer.ReadCloser(bracket); return info; } private static Ellipsoid ReadEllipsoid(WktStreamTokenizer tokenizer) { //SPHEROID["Airy 1830",6377563.396,299.3249646,AUTHORITY["EPSG","7001"]] - tokenizer.ReadToken("["); + var bracket = tokenizer.ReadOpener(); string name = tokenizer.ReadDoubleQuotedWord(); tokenizer.ReadToken(","); tokenizer.NextToken(); @@ -265,7 +277,7 @@ private static Ellipsoid ReadEllipsoid(WktStreamTokenizer tokenizer) if (tokenizer.GetStringValue() == ",") //Read authority { tokenizer.ReadAuthority(out authority, out authorityCode); - tokenizer.ReadToken("]"); + tokenizer.ReadCloser(bracket); } var ellipsoid = new Ellipsoid(majorAxis, 0.0, e, true, LinearUnit.Metre, name, authority, authorityCode, string.Empty, string.Empty, string.Empty); return ellipsoid; @@ -275,7 +287,7 @@ private static IProjection ReadProjection(WktStreamTokenizer tokenizer) { if (tokenizer.GetStringValue() != "PROJECTION") tokenizer.ReadToken("PROJECTION"); - tokenizer.ReadToken("[");//[ + var bracket = tokenizer.ReadOpener(); string projectionName = tokenizer.ReadDoubleQuotedWord(); string authority = string.Empty; long authorityCode = -1L; @@ -284,20 +296,22 @@ private static IProjection ReadProjection(WktStreamTokenizer tokenizer) if (tokenizer.GetStringValue() == ",") { tokenizer.ReadAuthority(out authority, out authorityCode); - tokenizer.ReadToken("]"); + tokenizer.ReadCloser(bracket); } + else + tokenizer.CheckCloser(bracket); tokenizer.ReadToken(",");//, tokenizer.ReadToken("PARAMETER"); var paramList = new List(); while (tokenizer.GetStringValue() == "PARAMETER") { - tokenizer.ReadToken("["); + bracket = tokenizer.ReadOpener(); string paramName = tokenizer.ReadDoubleQuotedWord(); tokenizer.ReadToken(","); tokenizer.NextToken(); double paramValue = tokenizer.GetNumericValue(); - tokenizer.ReadToken("]"); + tokenizer.ReadCloser(bracket); paramList.Add(new ProjectionParameter(paramName, paramValue)); //tokenizer.ReadToken(","); //tokenizer.NextToken(); @@ -338,7 +352,7 @@ private static ProjectedCoordinateSystem ReadProjectedCoordinateSystem(WktStream AUTHORITY["EPSG","27700"] ] */ - tokenizer.ReadToken("["); + var bracket = tokenizer.ReadOpener(); string name = tokenizer.ReadDoubleQuotedWord(); tokenizer.ReadToken(","); tokenizer.ReadToken("GEOGCS"); @@ -356,7 +370,10 @@ private static ProjectedCoordinateSystem ReadProjectedCoordinateSystem(WktStream switch (tokenizer.GetStringValue()) { case ",": + break; case "]": + case ")": + tokenizer.CheckCloser(bracket); break; case "PROJECTION": projection = ReadProjection(tokenizer); @@ -372,7 +389,7 @@ private static ProjectedCoordinateSystem ReadProjectedCoordinateSystem(WktStream break; case "AUTHORITY": tokenizer.ReadAuthority(out authority, out authorityCode); - //tokenizer.ReadToken("]"); + //tokenizer.ReadCloser(bracket); break; } ct = tokenizer.NextToken(); @@ -394,7 +411,7 @@ private static GeocentricCoordinateSystem ReadGeocentricCoordinateSystem(WktStre * GEOCCS["", , , {,, , } {,}] */ - tokenizer.ReadToken("["); + var bracket = tokenizer.ReadOpener(); string name = tokenizer.ReadDoubleQuotedWord(); tokenizer.ReadToken(","); tokenizer.ReadToken("DATUM"); @@ -424,7 +441,7 @@ private static GeocentricCoordinateSystem ReadGeocentricCoordinateSystem(WktStre if (tokenizer.GetStringValue() == "AUTHORITY") { tokenizer.ReadAuthority(out authority, out authorityCode); - tokenizer.ReadToken("]"); + tokenizer.ReadCloser(bracket); } } @@ -451,7 +468,7 @@ private static GeographicCoordinateSystem ReadGeographicCoordinateSystem(WktStre AUTHORITY["EPSG","4277"] ] */ - tokenizer.ReadToken("["); + var bracket = tokenizer.ReadOpener(); string name = tokenizer.ReadDoubleQuotedWord(); tokenizer.ReadToken(","); tokenizer.ReadToken("DATUM"); @@ -480,7 +497,7 @@ private static GeographicCoordinateSystem ReadGeographicCoordinateSystem(WktStre if (tokenizer.GetStringValue() == "AUTHORITY") { tokenizer.ReadAuthority(out authority, out authorityCode); - tokenizer.ReadToken("]"); + tokenizer.ReadCloser(bracket); } } @@ -502,7 +519,7 @@ private static HorizontalDatum ReadHorizontalDatum(WktStreamTokenizer tokenizer) string authority = string.Empty; long authorityCode = -1; - tokenizer.ReadToken("["); + var bracket = tokenizer.ReadOpener(); string name = tokenizer.ReadDoubleQuotedWord(); tokenizer.ReadToken(","); tokenizer.ReadToken("SPHEROID"); @@ -519,7 +536,7 @@ private static HorizontalDatum ReadHorizontalDatum(WktStreamTokenizer tokenizer) else if (tokenizer.GetStringValue() == "AUTHORITY") { tokenizer.ReadAuthority(out authority, out authorityCode); - tokenizer.ReadToken("]"); + tokenizer.ReadCloser(bracket); } } // make an assumption about the datum type. @@ -531,7 +548,7 @@ private static HorizontalDatum ReadHorizontalDatum(WktStreamTokenizer tokenizer) private static PrimeMeridian ReadPrimeMeridian(WktStreamTokenizer tokenizer) { //PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]] - tokenizer.ReadToken("["); + var bracket = tokenizer.ReadOpener(); string name = tokenizer.ReadDoubleQuotedWord(); tokenizer.ReadToken(","); tokenizer.NextToken(); @@ -543,8 +560,11 @@ private static PrimeMeridian ReadPrimeMeridian(WktStreamTokenizer tokenizer) if (tokenizer.GetStringValue() == ",") { tokenizer.ReadAuthority(out authority, out authorityCode); - tokenizer.ReadToken("]"); + tokenizer.ReadCloser(bracket); } + else + tokenizer.CheckCloser(bracket); + // make an assumption about the Angular units - degrees. var primeMeridian = new PrimeMeridian(longitude, AngularUnit.Degrees, name, authority, authorityCode, string.Empty, string.Empty, string.Empty); @@ -572,7 +592,7 @@ private static FittedCoordinateSystem ReadFittedCoordinateSystem (WktStreamToken AUTHORITY["CUSTOM","12345"] ] */ - tokenizer.ReadToken ("["); + var bracket = tokenizer.ReadOpener(); string name = tokenizer.ReadDoubleQuotedWord (); tokenizer.ReadToken (","); tokenizer.ReadToken ("PARAM_MT"); @@ -590,11 +610,15 @@ private static FittedCoordinateSystem ReadFittedCoordinateSystem (WktStreamToken switch (tokenizer.GetStringValue ()) { case ",": + break; case "]": + case ")": + tokenizer.CheckCloser(bracket); + break; case "AUTHORITY": tokenizer.ReadAuthority (out authority, out authorityCode); - //tokenizer.ReadToken("]"); + //tokenizer.ReadCloser(bracket); break; } ct = tokenizer.NextToken (); diff --git a/src/ProjNet/IO/CoordinateSystems/WKTStreamTokenizer.cs b/src/ProjNet/IO/CoordinateSystems/WKTStreamTokenizer.cs index 0c9ec49..c6fa98b 100644 --- a/src/ProjNet/IO/CoordinateSystems/WKTStreamTokenizer.cs +++ b/src/ProjNet/IO/CoordinateSystems/WKTStreamTokenizer.cs @@ -91,6 +91,64 @@ public string ReadDoubleQuotedWord() return word; } + /// + /// Reads an opener + /// + /// The expected bracket type. + /// The bracket type encountered + public WktBracket ReadOpener(WktBracket expectedBracket = WktBracket.DontCare) + { + NextToken(); + string stringValue = GetStringValue(); + if (stringValue == "[") + { + if (expectedBracket == WktBracket.Square || expectedBracket == WktBracket.DontCare) + return WktBracket.Square; + } + else if (stringValue == "(") + { + if (expectedBracket == WktBracket.Round || expectedBracket == WktBracket.DontCare) + return WktBracket.Round; + } + + string expectedToken = expectedBracket == WktBracket.Square ? "[" : "("; + string s = string.Format(_nfi, "Expecting ('{3}') but got a '{0}' at line {1} column {2}.", stringValue, LineNumber, Column, expectedToken); + throw new ArgumentException(s); + } + + /// + /// Reads an closer + /// + /// The expected bracket type. + public void ReadCloser(WktBracket expectedBracket) + { + NextToken(); + CheckCloser(expectedBracket); + } + + /// + /// Checks if the current token is a closer of expected type. + /// + /// The expected bracket type. + public void CheckCloser(WktBracket expectedBracket) + { + string stringValue = GetStringValue(); + if (stringValue == "]") + { + if (expectedBracket == WktBracket.Square || expectedBracket == WktBracket.DontCare) + return; + } + else if (stringValue == ")") + { + if (expectedBracket == WktBracket.Round || expectedBracket == WktBracket.DontCare) + return; + } + + string expectedToken = expectedBracket == WktBracket.Square ? "]" : ")"; + string s = string.Format(_nfi, "Expecting ('{3}') but got a '{0}' at line {1} column {2}.", stringValue, LineNumber, Column, expectedToken); + throw new ArgumentException(s); + } + /// /// Reads the authority and authority code. /// @@ -101,7 +159,7 @@ public void ReadAuthority(out string authority, out long authorityCode) //AUTHORITY["EPGS","9102"]] if (GetStringValue() != "AUTHORITY") ReadToken("AUTHORITY"); - ReadToken("["); + var bracket = ReadOpener(); authority = ReadDoubleQuotedWord(); ReadToken(","); NextToken(); @@ -109,7 +167,7 @@ public void ReadAuthority(out string authority, out long authorityCode) authorityCode = (long) GetNumericValue(); else long.TryParse(ReadDoubleQuotedWord(), NumberStyles.Any, _nfi, out authorityCode); - ReadToken("]"); + ReadCloser(bracket); } } } diff --git a/src/ProjNet/IO/CoordinateSystems/WktBracket.cs b/src/ProjNet/IO/CoordinateSystems/WktBracket.cs new file mode 100644 index 0000000..6cef3ac --- /dev/null +++ b/src/ProjNet/IO/CoordinateSystems/WktBracket.cs @@ -0,0 +1,39 @@ +// Copyright 2021 - NetTopologySuite - Team +// +// This file is part of ProjNet. +// ProjNet is free software; you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// ProjNet is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. + +// You should have received a copy of the GNU Lesser General Public License +// along with ProjNet; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +namespace ProjNet.IO.CoordinateSystems +{ + /// + /// An enumeration of possible bracket types + /// + internal enum WktBracket + { + /// + /// Bracket type not specified. + /// + DontCare, + /// + /// Opener "(", closer ")" + /// + Round, + /// + /// Opener "[", closer "]" + /// + Square, + //Brace + } +} diff --git a/src/ProjNet/ProjNET.csproj b/src/ProjNet/ProjNET.csproj index 2c3fc9a..e3d1e27 100644 --- a/src/ProjNet/ProjNET.csproj +++ b/src/ProjNet/ProjNET.csproj @@ -5,6 +5,7 @@ ProjNet netstandard2.0 true + true @@ -28,4 +29,11 @@ Proj.NET performs point-to-point coordinate conversions between geodetic coordin + + + + + + + diff --git a/test/ProjNet.Tests/WKT/WKTCoordSysParserTests.cs b/test/ProjNet.Tests/WKT/WKTCoordSysParserTests.cs index 97bc3cb..8c17209 100644 --- a/test/ProjNet.Tests/WKT/WKTCoordSysParserTests.cs +++ b/test/ProjNet.Tests/WKT/WKTCoordSysParserTests.cs @@ -84,6 +84,10 @@ public void TestProjectedCoordinateSystem_EPSG_2918() ProjectedCoordinateSystem pcs = null; Assert.That(() => pcs = _coordinateSystemFactory.CreateFromWkt(wkt) as ProjectedCoordinateSystem, Throws.Nothing); + ProjectedCoordinateSystem pcs2 = null; + Assert.That(() => pcs2 = _coordinateSystemFactory.CreateFromWkt(wkt.Replace("[", "(").Replace("]", ")")) as ProjectedCoordinateSystem, Throws.Nothing); + Assert.That(pcs.EqualParams(pcs2), Is.True); + Assert.That(pcs, Is.Not.Null, "Could not parse WKT: " + wkt); CheckInfo(pcs, "NAD83(HARN) / Texas Central (ftUS)", "EPSG", 2918); @@ -118,8 +122,10 @@ public void ParseAllWKTs() int parseCount = 0; foreach (var wkt in SRIDReader.GetSrids()) { - var cs = _coordinateSystemFactory.CreateFromWkt(wkt.Wkt); - Assert.IsNotNull(cs, "Could not parse WKT: " + wkt); + var cs1 = _coordinateSystemFactory.CreateFromWkt(wkt.Wkt); + Assert.IsNotNull(cs1, "Could not parse WKT: " + wkt); + var cs2 = _coordinateSystemFactory.CreateFromWkt(wkt.Wkt.Replace("[", "(").Replace("]", ")")); + Assert.That(cs1.EqualParams(cs2), Is.True); parseCount++; } Assert.That(parseCount, Is.GreaterThan(2671), "Not all WKT was parsed"); @@ -245,9 +251,13 @@ public void TestProjectedCoordinateSystem_EPSG27700_UnitBeforeProjection() "AXIS[\"Northing\",NORTH]]"; ProjectedCoordinateSystem pcs = null; - Assert.That(() => pcs = _coordinateSystemFactory.CreateFromWkt(wkt) as ProjectedCoordinateSystem, Throws.Nothing); + ProjectedCoordinateSystem pcs2 = null; + Assert.That(() => pcs2 = _coordinateSystemFactory.CreateFromWkt(wkt.Replace("[", "(").Replace("]", ")")) as ProjectedCoordinateSystem, Throws.Nothing); + Assert.That(pcs.EqualParams(pcs2), Is.True); + + CheckInfo(pcs, "OSGB 1936 / British National Grid", "EPSG", 27700); var gcs = pcs.GeographicCoordinateSystem;