diff --git a/ProjNet.IO.Wkt.Tests/Core/WktParserTests.cs b/ProjNet.IO.Wkt.Tests/Core/WktParserTests.cs new file mode 100644 index 0000000..41a1dc2 --- /dev/null +++ b/ProjNet.IO.Wkt.Tests/Core/WktParserTests.cs @@ -0,0 +1,124 @@ +using NUnit.Framework; +using Pidgin; +using ProjNet.IO.Wkt.Core; + +namespace ProjNet.IO.Wkt.Tests.Core; + +public class WktParserTests +{ + + [Test] + public void TestUnsignedIntegerParser() + { + // Arrange + string parserText01 = $@"210677"; + + // Act + uint parserResult01 = WktParser.UnsignedIntegerParser.ParseOrThrow(parserText01); + + // Assert + Assert.That(parserResult01, Is.EqualTo(210677)); + } + + [Test] + public void TestSignedIntegerParser() + { + // Arrange + string parserText01 = $@"+210677"; + string parserText02 = $@"-210677"; + string parserText03 = $@"210677"; + + // Act + int parserResult01 = WktParser.SignedIntegerParser.ParseOrThrow(parserText01); + int parserResult02 = WktParser.SignedIntegerParser.ParseOrThrow(parserText02); + int parserResult03 = WktParser.SignedIntegerParser.ParseOrThrow(parserText03); + + // Assert + Assert.That(parserResult01, Is.EqualTo(210677)); + Assert.That(parserResult02, Is.EqualTo(-210677)); + Assert.That(parserResult03, Is.EqualTo(210677)); + } + + + [Test] + public void TestSignedNumericLiteralParser() + { + // Arrange + string parserText01 = "-100.333333333333"; + string parserText02 = "+100.333333333333"; + string parserText03 = "100.333333333333"; + string parserText04 = ".333333333333"; + + // Act + double parserResult01 = WktParser.SignedNumericLiteralParser.ParseOrThrow(parserText01); + double parserResult02 = WktParser.SignedNumericLiteralParser.ParseOrThrow(parserText02); + double parserResult03 = WktParser.SignedNumericLiteralParser.ParseOrThrow(parserText03); + double parserResult04 = WktParser.SignedNumericLiteralParser.ParseOrThrow(parserText04); + + // Assert + Assert.That(parserResult01, Is.EqualTo(-100.333333333333)); + Assert.That(parserResult02, Is.EqualTo(100.333333333333)); + Assert.That(parserResult03, Is.EqualTo(100.333333333333)); + Assert.That(parserResult04, Is.EqualTo(0.333333333333)); + } + + [Test] + public void TestExactNumericLiteralParser() + { + // Arrange + string parserText01 = $@"21.043"; + string parserText02 = $@"0.043"; + string parserText03 = $@".043"; + + // Act + double parserResult01 = WktParser.ExactNumericLiteralParser.ParseOrThrow(parserText01); + double parserResult02 = WktParser.ExactNumericLiteralParser.ParseOrThrow(parserText02); + double parserResult03 = WktParser.ExactNumericLiteralParser.ParseOrThrow(parserText03); + + // Assert + Assert.That(parserResult01, Is.EqualTo(21.043d)); + Assert.That(parserResult02, Is.EqualTo(0.043d)); + Assert.That(parserResult03, Is.EqualTo(0.043d)); + } + + [Test] + public void TestApproximateNumericLiteralParser() + { + // Arrange + string parserText01 = $@"21.04E-3"; + string parserText02= $@"21.04E+3"; + string parserText03 = $@"21.04E3"; + string parserText04 = $@"0.04E3"; + string parserText05 = $@".04E3"; + + // Act + double parserResult01 = WktParser.ApproximateNumericLiteralParser.ParseOrThrow(parserText01); + double parserResult02 = WktParser.ApproximateNumericLiteralParser.ParseOrThrow(parserText02); + double parserResult03 = WktParser.ApproximateNumericLiteralParser.ParseOrThrow(parserText03); + double parserResult04 = WktParser.ApproximateNumericLiteralParser.ParseOrThrow(parserText04); + double parserResult05 = WktParser.ApproximateNumericLiteralParser.ParseOrThrow(parserText05); + + // Assert + Assert.That(parserResult01, Is.EqualTo(0.02104d)); + Assert.That(parserResult02, Is.EqualTo(21040d)); + Assert.That(parserResult03, Is.EqualTo(21040d)); + Assert.That(parserResult04, Is.EqualTo(40d)); + Assert.That(parserResult05, Is.EqualTo(40d)); + } + + [Test] + public void TestQuotedNameParser() + { + // Arrange + string str01 = "MyTextString"; + string parserText01 = $"\"{str01}\""; + + // Act + string parserResult01 = WktParser.QuotedNameParser.ParseOrThrow(parserText01); + + // Assert + Assert.That(parserResult01, Is.EqualTo(str01)); + } + + +} diff --git a/ProjNet4GeoAPI.sln b/ProjNet4GeoAPI.sln index 1da9334..0da5ecc 100644 --- a/ProjNet4GeoAPI.sln +++ b/ProjNet4GeoAPI.sln @@ -15,6 +15,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ProjNET.Tests", "test\ProjN EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ProjNet.Benchmark", "src\ProjNet.Benchmark\ProjNet.Benchmark.csproj", "{1E5D1CC5-8CFE-4C9D-9553-900469C57D6C}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ProjNet.IO.Wkt", "src\ProjNet.IO.Wkt\ProjNet.IO.Wkt.csproj", "{50AAB59B-25FB-4E36-A31A-B22C62B2105E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ProjNet.IO.Wkt.Tests", "ProjNet.IO.Wkt.Tests\ProjNet.IO.Wkt.Tests.csproj", "{3E23D15C-56F1-4431-B241-FC17D2F54BBF}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -33,6 +37,14 @@ Global {1E5D1CC5-8CFE-4C9D-9553-900469C57D6C}.Debug|Any CPU.Build.0 = Debug|Any CPU {1E5D1CC5-8CFE-4C9D-9553-900469C57D6C}.Release|Any CPU.ActiveCfg = Release|Any CPU {1E5D1CC5-8CFE-4C9D-9553-900469C57D6C}.Release|Any CPU.Build.0 = Release|Any CPU + {50AAB59B-25FB-4E36-A31A-B22C62B2105E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {50AAB59B-25FB-4E36-A31A-B22C62B2105E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {50AAB59B-25FB-4E36-A31A-B22C62B2105E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50AAB59B-25FB-4E36-A31A-B22C62B2105E}.Release|Any CPU.Build.0 = Release|Any CPU + {3E23D15C-56F1-4431-B241-FC17D2F54BBF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3E23D15C-56F1-4431-B241-FC17D2F54BBF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3E23D15C-56F1-4431-B241-FC17D2F54BBF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3E23D15C-56F1-4431-B241-FC17D2F54BBF}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/ProjNet.IO.Wkt/AssemblyInfo.cs b/src/ProjNet.IO.Wkt/AssemblyInfo.cs new file mode 100644 index 0000000..9e13281 --- /dev/null +++ b/src/ProjNet.IO.Wkt/AssemblyInfo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly:InternalsVisibleTo("ProjNet.IO.Wkt.Tests")] diff --git a/src/ProjNet.IO.Wkt/Core/DefaultWktOutputFormatter.cs b/src/ProjNet.IO.Wkt/Core/DefaultWktOutputFormatter.cs new file mode 100644 index 0000000..91d1dba --- /dev/null +++ b/src/ProjNet.IO.Wkt/Core/DefaultWktOutputFormatter.cs @@ -0,0 +1,198 @@ +using System; +using System.Globalization; +using System.Text; + +namespace ProjNet.IO.Wkt.Core +{ + /// + /// DefaultWktOutputFormatter - Keeping output compact with original delimiters. + /// + public class DefaultWktOutputFormatter : IWktOutputFormatter + { + private int indentCounter = 0; + + /// + public string Newline { get; } = null; + /// + public char? LeftDelimiter { get; } = null; + /// + public char? RightDelimiter { get; } = null; + + /// + public string Separator { get; } = null; + + /// + /// Indent chars. E.g. tab or spaces. Default null. + /// + public string Indent { get; } = null; + + /// + public string ExtraWhitespace { get; } = null; + + + /// + /// Constructor with support for optional overriding the settings. + /// + /// + /// + /// + /// + /// + public DefaultWktOutputFormatter( + string newline = null, + char? leftDelimiter = null, + char? rightDelimiter = null, + string indent = null, + string extraWhitespace = null) + { + Newline = newline; + LeftDelimiter = leftDelimiter; + RightDelimiter = rightDelimiter; + Indent = indent; + ExtraWhitespace = extraWhitespace; + } + + + /// + public IWktOutputFormatter AppendKeyword(string text, StringBuilder result, bool indent = true) + { + if (indent) + this.AppendIndent(result); + + result.Append(text); + + this.IncreaseIndentCounter(); + + return this; + } + + /// + public IWktOutputFormatter AppendSeparator(StringBuilder result, bool keepInside = false) + { + string s = Separator ?? ","; + result.Append(s); + if (!keepInside) + { + this.AppendNewline(result); + } + + return this; + } + + + /// + public IWktOutputFormatter Append(string text, StringBuilder result) + { + result.Append(text); + return this; + } + + /// + public IWktOutputFormatter Append(long l, StringBuilder result) + { + result.Append(l); + return this; + } + + /// + public IWktOutputFormatter Append(double d, StringBuilder result) + { + result.Append(d.ToString(CultureInfo.InvariantCulture)); + return this; + } + + /// + public IWktOutputFormatter AppendNewline(StringBuilder result) + { + if (!string.IsNullOrEmpty(Newline)) + { + result.Append(Newline); + } + + return this; + } + + /// + public IWktOutputFormatter AppendQuotedText(string text, StringBuilder result) + { + result.Append($"\"{text}\""); + return this; + } + + /// + public IWktOutputFormatter AppendLeftDelimiter(char original, StringBuilder result) + { + if (LeftDelimiter != null) + result.Append(LeftDelimiter); + else + result.Append(original); + return this; + } + + /// + public IWktOutputFormatter AppendRightDelimiter(char original, StringBuilder result) + { + //this.AppendIndent(result); + + if (RightDelimiter != null) + result.Append(RightDelimiter); + else + result.Append(original); + + this.DecreaseIndentCounter(); + //this.AppendNewline(result); + + return this; + } + + /// + public IWktOutputFormatter AppendExtraWhitespace(StringBuilder result) + { + if (!string.IsNullOrEmpty(ExtraWhitespace)) + { + result.Append(ExtraWhitespace); + } + + return this; + } + + /// + /// Increasing the indentCounter. + /// + /// + public IWktOutputFormatter IncreaseIndentCounter() + { + indentCounter++; + return this; + } + + /// + /// Decreasing the indentCounter. + /// + /// + public IWktOutputFormatter DecreaseIndentCounter() + { + indentCounter--; + return this; + } + + + /// + /// AppendIndent repeat Indent according to internal indentCounter. + /// + /// + /// + public IWktOutputFormatter AppendIndent(StringBuilder result) + { + if (!string.IsNullOrEmpty(Indent)) + { + for (int i = 1; i <= indentCounter; i++) + { + result.Append(Indent); + } + } + + return this; + } + } +} diff --git a/src/ProjNet.IO.Wkt/Core/IWktBuilder.cs b/src/ProjNet.IO.Wkt/Core/IWktBuilder.cs new file mode 100644 index 0000000..34723b6 --- /dev/null +++ b/src/ProjNet.IO.Wkt/Core/IWktBuilder.cs @@ -0,0 +1,435 @@ +using System.Collections.Generic; +using Pidgin; + +namespace ProjNet.IO.Wkt.Core +{ + /// + /// Interface for building/creating all Wkt related objects. + /// + public interface IWktBuilder + { + /// + /// Building the Authority object. + /// + /// + /// + /// + /// + /// + /// + /// + object BuildAuthority( + int offset, + string keyword, + char leftDelimiter, + string name, + int code, + char rightDelimiter + ); + + /// + /// Build the Axis object. + /// + /// + /// + /// + /// + /// + /// + /// + object BuildAxis( + int offset, + string keyword, + char leftDelimiter, + string name, + string direction, + char rightDelimiter); + + + /// + /// Build the Extension object. + /// + /// + /// + /// + /// + /// + /// + /// + object BuildExtension( + int offset, + string keyword, + char leftDelimiter, + string name, + string direction, + char rightDelimiter); + + + /// + /// Build the ToWgs84 object. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + object BuildToWgs84( + int offset, + string keyword, + char leftDelimiter, + double dxShift, + double dyShift, + double dzShift, + double exRotation, + double eyRotation, + double ezRotation, + double ppmScaling, + string description, + char rightDelimiter); + + + /// + /// Build the Projection object. + /// + /// + /// + /// + /// + /// + /// + /// + object BuildProjection( + int offset, + string keyword, + char leftDelimiter, + string name, + object authority, + char rightDelimiter); + + + + /// + /// Build the (Projection) Parameter. + /// + /// + /// + /// + /// + /// + /// + /// + object BuildProjectionParameter( + int offset, + string keyword, + char leftDelimiter, + string name, + double value, + char rightDelimiter); + + /// + /// Build the Ellipsoid object. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + object BuildEllipsoid( + int offset, + string keyword, + char leftDelimiter, + string name, + double semiMajorAxis, + double inverseFlattening, + object authority, + char rightDelimiter); + + + /// + /// Build the Spheroid object. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + object BuildSpheroid( + int offset, + string keyword, + char leftDelimiter, + string name, + double semiMajorAxis, + double inverseFlattening, + object authority, + char rightDelimiter); + + + /// + /// Build the primemeridian object. + /// + /// + /// + /// + /// + /// + /// + /// + /// + object BuildPrimeMeridian( + int offset, + string keyword, + char leftDelimiter, + string name, + double longitude, + object authority, + char rightDelimiter); + + + /// + /// Build a Unit object. + /// + /// + /// + /// + /// + /// + /// + /// + /// + object BuildUnit( + int offset, + string keyword, + char leftDelimiter, + string name, + double factor, + object authority, + char rightDelimiter); + + + /// + /// Build a LinearUnit object. + /// + /// + /// + /// + /// + /// + /// + /// + /// + object BuildLinearUnit( + int offset, + string keyword, + char leftDelimiter, + string name, + double factor, + object authority, + char rightDelimiter); + + + /// + /// Build an AngularUnit object. + /// + /// + /// + /// + /// + /// + /// + /// + /// + object BuildAngularUnit( + int offset, + string keyword, + char leftDelimiter, + string name, + double factor, + object authority, + char rightDelimiter); + + + /// + /// Build the Datum object. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + object BuildDatum( + int offset, + string keyword, + char leftDelimiter, + string name, + object spheroid, + object toWgs84, + object authority, + char rightDelimiter); + + + /// + /// Build the GeographicCoordinateSystem object. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + object BuildGeographicCoordinateSystem( + int offset, + string keyword, + char leftDelimiter, + string name, + object datum, + object meridian, + object unit, + IEnumerable axes, + object authority, + char rightDelimiter); + + + /// + /// Build a GeocentricCoordinateSystem object. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + object BuildGeocentricCoordinateSystem( + int offset, + string keyword, + char leftDelimiter, + string name, + object datum, + object meridian, + object unit, + IEnumerable axes, + object authority, + char rightDelimiter); + + + /// + /// Build the ProjectedCoordiniateSystem object. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + object BuildProjectedCoordinateSystem( + int offset, + string keyword, + char leftDelimiter, + string name, + object geogcs, + object projection, + object parameters, + object unit, + IEnumerable axes, + object extension, + object authority, + char rightDelimiter); + + + + /// + /// Build the MathTransform Parameter. + /// + /// + /// + /// + /// + /// + /// + /// + object BuildParameter( + int offset, + string keyword, + char leftDelimiter, + string name, + double value, + char rightDelimiter); + + /// + /// Build a ParameterMathTransform object. (Part of FittedCoordinateSystem). + /// + /// + /// + /// + /// + /// + /// + /// + object BuildParameterMathTransform( + int offset, + string keyword, + char leftDelimiter, + string name, + object parameters, + char rightDelimiter); + + + /// + /// Build the FittedCoordinateSystem object. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + object BuildFittedCoordinateSystem( + int offset, + string keyword, + char leftDelimiter, + string name, + object pmt, + object projcs, + object authority, + char rightDelimiter); + } +} diff --git a/src/ProjNet.IO.Wkt/Core/IWktOutputFormatter.cs b/src/ProjNet.IO.Wkt/Core/IWktOutputFormatter.cs new file mode 100644 index 0000000..2c95e0e --- /dev/null +++ b/src/ProjNet.IO.Wkt/Core/IWktOutputFormatter.cs @@ -0,0 +1,139 @@ +using System; +using System.Text; + +namespace ProjNet.IO.Wkt.Core +{ + + /// + /// IWktOutputFormatter interface for customizing ToString(...) support. + /// + public interface IWktOutputFormatter + { + /// + /// Newline char or empty string. Default empty. + /// + string Newline { get; } + + /// + /// LeftDelimiter if empty use original. Default empty. + /// + char? LeftDelimiter { get; } + /// + /// RightDelimiter if empty use original. Default empty. + /// + char? RightDelimiter { get; } + + /// + /// Separator to use. Default comma , + /// + string Separator { get; } + + /// + /// ExtraWhitespace. Default empty. + /// + string ExtraWhitespace { get; } + + + /// + /// Changeable Append method for Keywords. Optional extra indent is written. + /// + /// + /// + /// + /// + IWktOutputFormatter AppendKeyword(string text, StringBuilder result, bool indent = true); + + + /// + /// Changeable Append method for Separator. Optional extra newline afterward. + /// + /// + /// + /// + IWktOutputFormatter AppendSeparator(StringBuilder result, bool keepInside = false); + + /// + /// Changeable Append method for string. + /// + /// + /// + /// + IWktOutputFormatter Append(string text, StringBuilder result); + + /// + /// Changeable Append method for long. + /// + /// + /// + /// + IWktOutputFormatter Append(long l, StringBuilder result); + + /// + /// Changeable Append method for double. + /// + /// + /// + /// + IWktOutputFormatter Append(double d, StringBuilder result); + + + /// + /// Changeable AppendNewline method applying Newline. + /// + /// + /// + IWktOutputFormatter AppendNewline(StringBuilder result); + + /// + /// Changeable AppendQuotedText method applying text surrounded by double quotes. + /// + /// + /// + /// + IWktOutputFormatter AppendQuotedText(string text, StringBuilder result); + + /// + /// Changeable AppendLeftDelimiter applying LeftDelimiter or original. + /// + /// + /// + /// + IWktOutputFormatter AppendLeftDelimiter(char original, StringBuilder result); + + /// + /// Changeable AppendRightDelimiter applying RightDelimiter or original. + /// + /// + /// + /// + IWktOutputFormatter AppendRightDelimiter(char original, StringBuilder result); + + + /// + /// Changeable AppendExtraWhitespace optionally applying extr whitespace. + /// + /// + /// + IWktOutputFormatter AppendExtraWhitespace(StringBuilder result); + + /// + /// IncreaseIndentCounter + /// + /// + IWktOutputFormatter IncreaseIndentCounter(); + + /// + /// DecreaseIndentCounter + /// + /// + IWktOutputFormatter DecreaseIndentCounter(); + + + /// + /// AppendIndent + /// + /// + /// + IWktOutputFormatter AppendIndent(StringBuilder result); + } +} diff --git a/src/ProjNet.IO.Wkt/Core/IWktTraverseHandler.cs b/src/ProjNet.IO.Wkt/Core/IWktTraverseHandler.cs new file mode 100644 index 0000000..e80d5c2 --- /dev/null +++ b/src/ProjNet.IO.Wkt/Core/IWktTraverseHandler.cs @@ -0,0 +1,108 @@ +using System.Collections.Generic; +using ProjNet.IO.Wkt.Tree; + +namespace ProjNet.IO.Wkt.Core +{ + /// + /// IWktTraverseHandler interface for traveling a WktObject tree. + /// + public interface IWktTraverseHandler + { + + /// + /// Handle method for WktAuthority. + /// + /// + void Handle(WktAuthority authority); + + /// + /// Handle method for WktAxis. + /// + /// + void Handle(WktAxis axis); + + /// + /// Handle method for WktDatum. + /// + /// + void Handle(WktDatum datum); + + /// + /// Handle method for WktEllipsoid. + /// + /// + void Handle(WktEllipsoid ellipsoid); + + /// + /// Handle method for WktSpheroid. + /// + /// + void Handle(WktSpheroid spheroid); + + /// + /// Handle method for WktExtension. + /// + /// + void Handle(WktExtension extension); + + /// + /// Handle method for WktUnit. + /// + /// + void Handle(WktUnit unit); + + /// + /// Handle method for WktParameter. + /// + /// + void Handle(WktParameter parameter); + + /// + /// Handle method for WktParameterMathTransform. (FittedCoordinateSystem related). + /// + /// + void Handle(WktParameterMathTransform pmt); + + /// + /// Handle method for WktPrimeMeridian. + /// + /// + void Handle(WktPrimeMeridian meridian); + + /// + /// Handle method for WktProjection. + /// + /// + void Handle(WktProjection projection); + + /// + /// Handle method for WktToWgs84. + /// + /// + void Handle(WktToWgs84 toWgs84); + + /// + /// Handle method for WktGeocentricCoordinateSystem. + /// + /// + void Handle(WktGeocentricCoordinateSystem cs); + + /// + /// Handle method for WktGeographicCoordinateSystem. + /// + /// + void Handle(WktGeographicCoordinateSystem cs); + + /// + /// Handle method for WktProjectedCoordinateSystem. + /// + /// + void Handle(WktProjectedCoordinateSystem cs); + + /// + /// Handle method for WktFittedCoordinateSystem. + /// + /// + void Handle(WktFittedCoordinateSystem cs); + } +} diff --git a/src/ProjNet.IO.Wkt/Core/WktCRS1Parser.cs b/src/ProjNet.IO.Wkt/Core/WktCRS1Parser.cs new file mode 100644 index 0000000..3def1f3 --- /dev/null +++ b/src/ProjNet.IO.Wkt/Core/WktCRS1Parser.cs @@ -0,0 +1,606 @@ +using ProjNet.IO.Wkt.Tree; +using ProjNet.IO.Wkt.Utils; +using System; +using System.Linq; + +using static ProjNet.IO.Wkt.Utils.Utils; +using Pidgin; +using static Pidgin.Parser; + + + +namespace ProjNet.IO.Wkt.Core +{ + /// + /// WKT1Parser - Base parser for WKT 1 using Parser Combinator Library Pidgin. + /// + /// + public class WktCRS1Parser + { + + // 6.3.1 Basic characters + + // + internal static readonly Parser SimpleLatinUpperCaseLetter = OneOf( + Char('A'), Char('B'), Char('C'), + Char('D'), Char('E'), Char('F'), Char('G'), + Char('H'), Char('I'), Char('J'), Char('K'), + Char('L'), Char('M'), Char('N'), Char('O'), + Char('P'), Char('Q'), Char('R'), Char('S'), + Char('T'), Char('U'), Char('V'), Char('W'), + Char('X'), Char('Y'), Char('Z') + ); + + // + internal static readonly Parser SimpleLatinLowerCaseLetter = OneOf( + Char('a'), Char('b'), Char('c'), + Char('d'), Char('e'), Char('f'), Char('g'), + Char('h'), Char('i'), Char('j'), Char('k'), + Char('l'), Char('m'), Char('n'), Char('o'), + Char('p'), Char('q'), Char('r'), Char('s'), + Char('t'), Char('u'), Char('v'), Char('w'), + Char('x'), Char('y'), Char('z') + ); + + // + internal static readonly Parser Didgit = OneOf( + Char('0'), Char('1'), Char('2'), + Char('3'), Char('4'), Char('5'), + Char('6'), Char('7'), Char('8'), + Char('9') + ); + + // + internal static readonly Parser Space = Char(' '); + + // + internal static readonly Parser DoubleQuote = Char('"'); + + // + internal static readonly Parser NumberSign = Char('#'); + + // + internal static readonly Parser Percent = Char('%'); + + // + internal static readonly Parser Ampersand = Char('&'); + + // + internal static readonly Parser Quote = Char('\''); + + // + internal static readonly Parser LeftParen = Char('('); + // + internal static readonly Parser RightParen = Char(')'); + + // + internal static readonly Parser Asterisk = Char('*'); + + // + internal static readonly Parser PlusSign = Char('+'); + + // + internal static readonly Parser Comma = Char(','); + + // ::- + internal static readonly Parser MinusSign = Char('-'); + internal static readonly Parser Hyphen = Char('-'); + + // + internal static readonly Parser Period = Char('.'); + + // + internal static readonly Parser Solidus = Char('/'); + // + internal static readonly Parser ReverseSolidus = Char('\\'); + + // + internal static readonly Parser Colon = Char(':'); + // + internal static readonly Parser SemiColon = Char(';'); + + // + internal static readonly Parser LessThanOperator = Char('<'); + // + internal static readonly Parser EqualsOperator = Char('='); + // + internal static readonly Parser GreaterThanOperator = Char('>'); + + // + internal static readonly Parser QuestionMark = Char('?'); + + // + internal static readonly Parser LeftBracket = Char('['); + // + internal static readonly Parser RightBracket = Char(']'); + + // + internal static readonly Parser Circumflex = Char('^'); + + // + internal static readonly Parser Underscore = Char('_'); + + // + internal static readonly Parser LeftBrace = Char('{'); + // + internal static readonly Parser RightBrace = Char('}'); + + // + internal static readonly Parser VerticalBar = Char('|'); + + // + internal static readonly Parser DegreeSymbol = Char('\u00B0'); + + + // 6.3.2 Numbers + + // + internal static readonly Parser Sign = OneOf(PlusSign, MinusSign); + // + internal static readonly Parser UnsignedIntegerString = + WktCRS1Parser.Didgit.AtLeastOnceString(); + internal static readonly Parser UnsignedInteger = + UnsignedIntegerString + .Select(uint.Parse); + + // + internal static readonly Parser ExactNumericLiteralDotted = + Period + .Then(UnsignedIntegerString, (c, ui) => CalcAsFractionOf(0, ui)); + + internal static readonly Parser ExactNumericLiteral = + UnsignedInteger.Optional() + .Then(ExactNumericLiteralDotted.Optional(), (ui, d) => ui.GetValueOrDefault() + d.GetValueOrDefault()); + + + // + internal static readonly Parser SignedInteger = + Sign.Optional() + .Then(UnsignedInteger, (c, ui) => (int) ((c.HasValue && c.Value == '-' ? -1 : 1) * ui)); + + // + internal static readonly Parser Exponent = + SignedInteger; + + // + internal static readonly Parser Mantissa = + ExactNumericLiteral; + + // + internal static readonly Parser ApproximateNumericLiteralExp = + OneOf(Char('e'),Char('E')) + .Then(Exponent) + .Select(e => Math.Pow(10, e)); + internal static readonly Parser ApproximateNumericLiteral = + Mantissa.Then(ApproximateNumericLiteralExp.Optional(), (m, e) => m * (e.HasValue ? e.Value : 1)); + + // + internal static readonly Parser UnsignedNumericLiteral = OneOf( + ApproximateNumericLiteral, + ExactNumericLiteral + ); + + // + internal static readonly Parser SignedNumericLiteral = + Sign.Optional() + .Then(UnsignedNumericLiteral, (s, d) => (double) ((s.HasValue && s.Value == '-' ? -1 : 1) * d)); + + + // + internal static readonly Parser Number = OneOf( + SignedNumericLiteral, + UnsignedNumericLiteral) + .Labelled("number"); + + + // 6.3.3 Date and time + + // ::= !! two digits + internal static readonly Parser Day = WktCRS1Parser.Didgit + .Repeat(2) + .Select(d => uint.Parse(new string(d.ToArray()))); + // ::= !! two digits + internal static readonly Parser Month = WktCRS1Parser.Didgit + .Repeat(2) + .Select(m => uint.Parse(new string(m.ToArray()))); + // ::= !! four digits + internal static readonly Parser Year = WktCRS1Parser.Didgit + .Repeat(4) + .Select(y => uint.Parse(new string(y.ToArray()))); + + // ::= + // !! two digits including leading zero if less than 10 + internal static readonly Parser Hour = WktCRS1Parser.Didgit + .Repeat(2) + .Select(h => uint.Parse(new string(h.ToArray()))); + + // ::= + // !! two digits including leading zero if less than 10 + internal static readonly Parser Minute = WktCRS1Parser.Didgit + .Repeat(2) + .Select(h => uint.Parse(new string(h.ToArray()))); + + // ::= + // !! two digits including leading zero if less than 10 + internal static readonly Parser SecondsInteger = WktCRS1Parser.Didgit + .Repeat(2) + .Select(h => uint.Parse(new string(h.ToArray()))); + // ::= + internal static readonly Parser SecondsFraction = UnsignedInteger; + + // ::= [ [ ] ] + // !! In this International Standard the separator between the integer and fractional parts of a second value shall be a period. The ISO 8601 preference for comma is not permitted. + internal static readonly Parser SecondsDotted = Period.Then(SecondsFraction); + + internal static readonly Parser Second = SecondsInteger.Then(SecondsDotted.Optional(), + (u, mf) => new DateTimeBuilder() + .SetSeconds((int) u) + .SetMilliseconds((int) mf.GetValueOrDefault())); + + // ::= Z + internal static readonly Parser UtcDesignator = Char('Z'); + + // ::= { | } [ ] + internal static readonly Parser ColonMinute = Colon.Then(Minute); + internal static readonly Parser LocalTimeZoneDesignator = Sign + .Then(Hour, (ms, u) => (ms == '-' ? -1 : 1) * u) + .Then(ColonMinute.Optional(), (h, mm) => new TimeSpan(0, (int) h, (int) mm.GetValueOrDefault(), 0)); + + //