diff --git a/src/ProjNet/CoordinateSystems/CoordinateSystemFactory.cs b/src/ProjNet/CoordinateSystems/CoordinateSystemFactory.cs index 28d831d..d9e0b69 100644 --- a/src/ProjNet/CoordinateSystems/CoordinateSystemFactory.cs +++ b/src/ProjNet/CoordinateSystems/CoordinateSystemFactory.cs @@ -5,7 +5,7 @@ // 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 @@ -13,12 +13,13 @@ // 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 +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA using System; using System.Collections.Generic; using System.Text; using ProjNet.IO.CoordinateSystems; +using ProjNet.IO.Wkt; namespace ProjNet.CoordinateSystems { @@ -26,15 +27,15 @@ namespace ProjNet.CoordinateSystems /// Builds up complex objects from simpler objects or values. /// /// - /// CoordinateSystemFactory allows applications to make coordinate systems that + /// CoordinateSystemFactory allows applications to make coordinate systems that /// is very flexible, whereas the other factories are easier to use. /// So this Factory can be used to make 'special' coordinate systems. - /// For example, the EPSG authority has codes for USA state plane coordinate systems - /// using the NAD83 datum, but these coordinate systems always use meters. EPSG does not + /// For example, the EPSG authority has codes for USA state plane coordinate systems + /// using the NAD83 datum, but these coordinate systems always use meters. EPSG does not /// have codes for NAD83 state plane coordinate systems that use feet units. This factory /// lets an application create such a hybrid coordinate system. /// - public class CoordinateSystemFactory + public class CoordinateSystemFactory { /// /// Creates an instance of this class @@ -64,6 +65,12 @@ public CoordinateSystem CreateFromWkt(string WKT) return info as CoordinateSystem; } + public CoordinateSystem CreateFromWktNew(string WKT) + { + var info = WktToProjBuilder.ParseAndBuild(WKT); + return info as CoordinateSystem; + } + /// /// Creates a [NOT IMPLEMENTED]. @@ -83,11 +90,11 @@ public CompoundCoordinateSystem CreateCompoundCoordinateSystem(string name, Coor /// /// Creates a . /// - /// The units of the axes in the fitted coordinate system will be + /// The units of the axes in the fitted coordinate system will be /// inferred from the units of the base coordinate system. If the affine map /// performs a rotation, then any mixed axes must have identical units. For - /// example, a (lat_deg,lon_deg,height_feet) system can be rotated in the - /// (lat,lon) plane, since both affected axes are in degrees. But you + /// example, a (lat_deg,lon_deg,height_feet) system can be rotated in the + /// (lat,lon) plane, since both affected axes are in degrees. But you /// should not rotate this coordinate system in any other plane. /// Name of coordinate system /// Base coordinate system @@ -122,9 +129,9 @@ public FittedCoordinateSystem CreateFittedCoordinateSystem(string name, Coordina /// Creates a local coordinate system. /// /// - /// The dimension of the local coordinate system is determined by the size of - /// the axis array. All the axes will have the same units. If you want to make - /// a coordinate system with mixed units, then you can make a compound + /// The dimension of the local coordinate system is determined by the size of + /// the axis array. All the axes will have the same units. If you want to make + /// a coordinate system with mixed units, then you can make a compound /// coordinate system from different local coordinate systems. /// /// Name of local coordinate system @@ -219,9 +226,9 @@ public IProjection CreateProjection(string name, string wktProjectionClass, List /// Creates from ellipsoid and Bursa-World parameters. /// /// - /// Since this method contains a set of Bursa-Wolf parameters, the created + /// Since this method contains a set of Bursa-Wolf parameters, the created /// datum will always have a relationship to WGS84. If you wish to create a - /// horizontal datum that has no relationship with WGS84, then you can + /// horizontal datum that has no relationship with WGS84, then you can /// either specify a horizontalDatumType of , or create it via WKT. /// /// Name of ellipsoid @@ -294,7 +301,7 @@ public ILocalDatum CreateLocalDatum(string name, DatumType datumType) /// /// Name of datum /// Type of datum - /// Vertical datum + /// Vertical datum public VerticalDatum CreateVerticalDatum(string name, DatumType datumType) { if (string.IsNullOrWhiteSpace(name)) @@ -322,7 +329,7 @@ public VerticalCoordinateSystem CreateVerticalCoordinateSystem(string name, Vert } /// - /// Creates a from a datum, + /// Creates a from a datum, /// linear unit and . /// /// Name of geocentric coordinate system diff --git a/src/ProjNet/IO/Wkt/WktCrs.g4 b/src/ProjNet/IO/Wkt/WktCrs.g4 new file mode 100644 index 0000000..103b652 --- /dev/null +++ b/src/ProjNet/IO/Wkt/WktCrs.g4 @@ -0,0 +1,275 @@ +/* + [The "BSD licence"] Copyright (c) 2023 Nikolay Fiykov All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above + copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided with the distribution. 3. The name of the author may not be used to endorse or promote products derived from this software without + specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * For parsing propeties file (like GeoTools epsg.properties), use starting rule "propsFile". For parsing single WKT CRS definition, use starting rule "wkt". + */ + +// $antlr-format alignTrailingComments true, columnLimit 150, minEmptyLines 1, maxEmptyLinesToKeep 1, reflowComments false, useTab false +// $antlr-format allowShortRulesOnASingleLine false, allowShortBlocksOnASingleLine true, alignSemicolons hanging, alignColons hanging + +grammar WktCrs; + + +propsFile + : propRow* EOF + ; + +propRow + : commentLine + | epsgDefLine + ; + +commentLine + : COMMENT_LINE + ; + +epsgDefLine + : epsgCode EQ wkt + ; + +wkt + : compdcs + | projcs + | geogcs + | vertcs + | geoccs + | localcs + | fittedcs + ; + +fittedcs + : 'FITTED_CS' LPAR name COMMA paramsmt COMMA projcs (COMMA authority)? RPAR + ; + +paramsmt + : 'PARAM_MT' LPAR name (COMMA parameter)+ RPAR + ; + +compdcs + : 'COMPD_CS' LPAR name COMMA (projcs | geogcs) COMMA vertcs COMMA authority RPAR + ; + +projcs + : 'PROJCS' LPAR name COMMA geogcs COMMA projection COMMA (parameter COMMA)+ unit COMMA ((extension COMMA) | (axis COMMA))* authority RPAR + ; + +geoccs + : 'GEOCCS' LPAR name COMMA datum COMMA primem COMMA unit COMMA (axis COMMA)+ authority RPAR + ; + +geogcs + : 'GEOGCS' LPAR name COMMA datum COMMA primem COMMA unit (COMMA axis)* (COMMA authority)? RPAR + ; + +vertcs + : 'VERT_CS' LPAR name COMMA vertdatum COMMA unit COMMA axis COMMA authority RPAR + ; + +localcs + : 'LOCAL_CS' LPAR name COMMA localdatum COMMA unit COMMA (axis COMMA)+ authority RPAR + ; + +datum + : 'DATUM' LPAR name COMMA spheroid ((COMMA towgs84) | (COMMA authority))* RPAR + ; + +vertdatum + : 'VERT_DATUM' LPAR name COMMA type COMMA authority RPAR + ; + +localdatum + : 'LOCAL_DATUM' LPAR name COMMA type (COMMA authority)? RPAR + ; + +spheroid + : 'SPHEROID' LPAR name COMMA semiMajorAxis COMMA inverseFlattening (COMMA authority)? RPAR + ; + +towgs84 + : 'TOWGS84' LPAR dx COMMA dy COMMA dz (COMMA ex COMMA ey COMMA ez (COMMA ppm)?)? RPAR + ; + +extension + : 'EXTENSION' LPAR name COMMA projtext RPAR + ; + +authority + : 'AUTHORITY' LPAR authorityName COMMA code RPAR + ; + +primem + : 'PRIMEM' LPAR name COMMA longitude (COMMA unit)? (COMMA authority)? RPAR + ; + +unit + : 'UNIT' LPAR name COMMA conversionFactor (COMMA authority)? RPAR + ; + +axis + : 'AXIS' LPAR name COMMA axisOrient RPAR + ; + +projection + : 'PROJECTION' LPAR name (COMMA authority)? RPAR + ; + +parameter + : 'PARAMETER' LPAR name COMMA value RPAR + ; + +authorityName + : '"EPSG"' + | '"ESRI"' + ; + +axisOrient + : 'EAST' + | 'WEST' + | 'NORTH' + | 'SOUTH' + | 'NORTH_EAST' + | 'NORTH_WEST' + | 'UP' + | 'DOWN' + | 'OTHER' + | 'GEOCENTRIC_X' + | 'GEOCENTRIC_Y' + | 'GEOCENTRIC_Z' + | name + ; + +epsgCode + : PKEY + | NUMBER + ; + +name + : TEXT + ; + +number + : NUMBER + ; + +type + : NUMBER + ; + +semiMajorAxis + : NUMBER + ; + +inverseFlattening + : NUMBER + ; + +dx + : NUMBER + ; + +dy + : NUMBER + ; + +dz + : NUMBER + ; + +ex + : NUMBER + ; + +ey + : NUMBER + ; + +ez + : NUMBER + ; + +ppm + : NUMBER + ; + +projtext + : TEXT + ; + +code + : TEXT + | NUMBER + ; + +longitude + : NUMBER + ; + +conversionFactor + : NUMBER + ; + +value + : NUMBER + ; + +NUMBER + : PM? INT ('.' INT)? EXP? + ; + +TEXT + : '"' ('""' | ~'"')* '"' + ; + +PKEY + : [A-Z] [0-9A-Z]+ + ; + +COMMENT_LINE + : '#' ~[\r\n]* + ; + +WS + : [ \r\n\t]+ -> skip + ; + +COMMA + : ',' + ; + +LPAR + : '[' + | '(' + ; + +RPAR + : ']' + | ')' + ; + +EQ + : '=' + ; + +fragment INT + : [0-9]+ + ; + +fragment EXP + : [Ee] PM? INT + ; + +fragment PM + : '+' + | '-' + ; \ No newline at end of file diff --git a/src/ProjNet/IO/Wkt/WktToProjBuilder.cs b/src/ProjNet/IO/Wkt/WktToProjBuilder.cs new file mode 100644 index 0000000..7f444fd --- /dev/null +++ b/src/ProjNet/IO/Wkt/WktToProjBuilder.cs @@ -0,0 +1,1144 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; +using Antlr4.Runtime; +using Antlr4.Runtime.Atn; +using Antlr4.Runtime.Misc; +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Transformations; + +namespace ProjNet.IO.Wkt +{ + /// + /// WktToProjBuilder + /// + public partial class WktToProjBuilder + { + private readonly CoordinateSystemFactory factory; + + /// + /// Constructor. + /// + public WktToProjBuilder() + { + factory = new CoordinateSystemFactory(); + } + + + internal class Authority + { + public string Name { get; set; } + + public int Code { get; set; } + } + + + internal class NameVisitor : WktCrsBaseVisitor + { + public static readonly NameVisitor Instance = new NameVisitor(); + + public override string VisitName(WktCrsParser.NameContext context) + { + return context.GetText().Trim(new char[] {'\"', ' '}); + } + } + + + internal class ValueVisitor : WktCrsBaseVisitor + { + public static readonly ValueVisitor Instance = new ValueVisitor(); + + public override double VisitValue(WktCrsParser.ValueContext context) + { + string valueStr = context.GetText(); + if (double.TryParse(valueStr, NumberStyles.Any, CultureInfo.InvariantCulture, out double d)) + return d; + + return double.NaN; + } + } + + + /// + /// LongitudeVisitor + /// + public class LongitudeVisitor : WktCrsBaseVisitor + { + /// + /// VisitLongitude + /// + /// + /// + public override double VisitLongitude(WktCrsParser.LongitudeContext context) + { + string valueStr = context.GetText(); + if (double.TryParse(valueStr, NumberStyles.Any, CultureInfo.InvariantCulture, out double d)) + return d; + + return double.NaN; + } + } + + internal class AuthorityNameVisitor : WktCrsBaseVisitor + { + public static readonly AuthorityNameVisitor Instance = new AuthorityNameVisitor(); + + public override string VisitAuthorityName(WktCrsParser.AuthorityNameContext context) + { + return context.GetText().Trim(new char[] {'\"', ' '}); + } + } + + + internal class AuthorityCodeVisitor : WktCrsBaseVisitor + { + public static readonly AuthorityCodeVisitor Instance = new AuthorityCodeVisitor(); + + public override int VisitCode(WktCrsParser.CodeContext context) + { + if (!context.IsEmpty) + { + string codeStr = context.GetText(); + codeStr = codeStr.Trim(new char[] {' ', '\"'}); + if (codeStr.Contains("_")) + { + codeStr = codeStr.Substring(0, codeStr.IndexOf('_')); + } + + if (!string.IsNullOrWhiteSpace(codeStr) && int.TryParse(codeStr, NumberStyles.Any, + CultureInfo.InvariantCulture, out int nmbr)) + { + return nmbr; + } + } + + return -1; + } + } + + + internal class ProjTextVisitor : WktCrsBaseVisitor + { + public override string VisitProjtext(WktCrsParser.ProjtextContext context) + { + string str = context.GetText(); + str = str.Trim(new char[] {' ', '\"'}); + return str; + } + } + + internal class AxisOrientVisitor : WktCrsBaseVisitor + { + public override AxisOrientationEnum VisitAxisOrient(WktCrsParser.AxisOrientContext context) + { + + if (!context.IsEmpty) + { + string direction = context.GetText().Trim(new char[] {' ', '\"'}); + if (Enum.TryParse(direction, true, out AxisOrientationEnum enumVal)) + { + return enumVal; + } + } + + return AxisOrientationEnum.Other; + } + } + + + internal class AuthorityVisitor : WktCrsBaseVisitor + { + public static readonly AuthorityVisitor Instance = new AuthorityVisitor(); + + public override Authority VisitAuthority([NotNull] WktCrsParser.AuthorityContext context) + { + string authName = string.Empty; + var authNameCtx = context.authorityName(); + if (authNameCtx != null) + { + var visitor = AuthorityNameVisitor.Instance; + authName = visitor.VisitAuthorityName(authNameCtx); + } + + int code = -1; + var authCodeCtx = context.code(); + if (authCodeCtx != null) + { + var visitor = AuthorityCodeVisitor.Instance; + code = visitor.VisitCode(authCodeCtx); + } + + return new Authority {Name = authName, Code = code}; + } + } + + internal class AxisVisitor : WktCrsBaseVisitor + { + public static readonly AxisVisitor Instance = new AxisVisitor(); + + public override AxisInfo VisitAxis(WktCrsParser.AxisContext context) + { + string name = string.Empty; + var nameCtx = context.name(); + if (nameCtx != null) + { + var visitor = NameVisitor.Instance; + name = visitor.VisitName(nameCtx); + } + + var orient = AxisOrientationEnum.Other; + var axisOrientCtx = context.axisOrient(); + if (axisOrientCtx != null) + { + var visitor = new AxisOrientVisitor(); + orient = visitor.VisitAxisOrient(axisOrientCtx); + } + + return new AxisInfo(name, orient); + } + } + + internal class ExtensionVisitor : WktCrsBaseVisitor<(string, string)> + { + public static readonly ExtensionVisitor Instance = new ExtensionVisitor(); + + public override (string, string) VisitExtension([NotNull] WktCrsParser.ExtensionContext context) + { + string name = string.Empty; + var nameCtx = context.name(); + if (nameCtx != null) + { + var visitor = NameVisitor.Instance; + name = visitor.VisitName(nameCtx); + } + + string projText = string.Empty; + var projCtx = context.projtext(); + if (projCtx != null) + { + var visitor = new ProjTextVisitor(); + projText = visitor.VisitProjtext(projCtx); + } + + // No ProjNet object for Extension so returning a tuple. + return (name, projText); + } + } + + + internal class ToWgs84Visitor : WktCrsBaseVisitor + { + public static readonly ToWgs84Visitor Instance = new ToWgs84Visitor(); + + public override Wgs84ConversionInfo VisitTowgs84(WktCrsParser.Towgs84Context context) + { + double dx = 0.0d; + var dxCtx = context.dx(); + if (!dxCtx.IsEmpty && double.TryParse(dxCtx.GetText(), NumberStyles.Any, + CultureInfo.InvariantCulture, out double dxResult)) + dx = dxResult; + + double dy = 0.0d; + var dyCtx = context.dy(); + if (!dyCtx.IsEmpty && double.TryParse(dyCtx.GetText(), NumberStyles.Any, + CultureInfo.InvariantCulture, out double dyResult)) + dy = dyResult; + + double dz = 0.0d; + var dzCtx = context.dz(); + if (!dzCtx.IsEmpty && double.TryParse(dzCtx.GetText(), NumberStyles.Any, + CultureInfo.InvariantCulture, out double dzResult)) + dz = dzResult; + + double ex = 0.0d; + var exCtx = context.ex(); + if (!exCtx.IsEmpty && double.TryParse(exCtx.GetText(), NumberStyles.Any, + CultureInfo.InvariantCulture, out double exResult)) + ex = exResult; + + double ey = 0.0d; + var eyCtx = context.ey(); + if (!eyCtx.IsEmpty && double.TryParse(eyCtx.GetText(), NumberStyles.Any, + CultureInfo.InvariantCulture, out double eyResult)) + ey = eyResult; + + double ez = 0.0d; + var ezCtx = context.ez(); + if (!ezCtx.IsEmpty && double.TryParse(ezCtx.GetText(), NumberStyles.Any, + CultureInfo.InvariantCulture, out double ezResult)) + ez = ezResult; + + double ppm = 0.0d; + var ppmCtx = context.ppm(); + if (ppmCtx.IsEmpty && double.TryParse(ppmCtx.GetText(), NumberStyles.Any, + CultureInfo.InvariantCulture, out double ppmResult)) + ppm = ppmResult; + + return new Wgs84ConversionInfo(dx, dy, dz, ex, ey, ez, ppm); + } + } + + + internal class ProjectionVisitor : WktCrsBaseVisitor + { + public static readonly ProjectionVisitor Instance = new ProjectionVisitor(); + + public override Projection VisitProjection(WktCrsParser.ProjectionContext context) + { + string name = string.Empty; + var nameCtx = context.name(); + if (nameCtx != null) + { + var visitor = NameVisitor.Instance; + name = visitor.VisitName(nameCtx); + } + + Authority authority = null; + var authorityCtx = context.authority(); + if (authorityCtx != null) + { + var visitor = AuthorityVisitor.Instance; + authority = visitor.VisitAuthority(authorityCtx); + } + + return new Projection(name, new List(), name, authority?.Name, + authority != null ? authority.Code : -1, string.Empty, string.Empty, string.Empty); + } + } + + internal class ProjectionParameterVisitor : WktCrsBaseVisitor + { + public static readonly ProjectionParameterVisitor Instance = new ProjectionParameterVisitor(); + + public override ProjectionParameter VisitParameter(WktCrsParser.ParameterContext context) + { + string name = string.Empty; + var nameCtx = context.name(); + if (nameCtx != null) + { + var visitor = NameVisitor.Instance; + name = visitor.VisitName(nameCtx); + } + + double value = double.NaN; + var valueCtx = context.value(); + if (valueCtx != null) + { + var visitor = ValueVisitor.Instance; + value = visitor.VisitValue(valueCtx); + } + + return new ProjectionParameter(name, value); + } + } + + internal class ParameterVisitor : WktCrsBaseVisitor + { + public static readonly ParameterVisitor Instance = new ParameterVisitor(); + + public override Parameter VisitParameter(WktCrsParser.ParameterContext context) + { + string name = string.Empty; + var nameCtx = context.name(); + if (nameCtx != null) + { + var visitor = NameVisitor.Instance; + name = visitor.VisitName(nameCtx); + } + + double value = double.NaN; + var valueCtx = context.value(); + if (valueCtx != null) + { + var visitor = ValueVisitor.Instance; + value = visitor.VisitValue(valueCtx); + } + + return new Parameter(name, value); + } + } + + internal class SemiMajorAxisVisitor : WktCrsBaseVisitor + { + public static readonly SemiMajorAxisVisitor Instance = new SemiMajorAxisVisitor(); + + public override double VisitSemiMajorAxis(WktCrsParser.SemiMajorAxisContext context) + { + if (!context.IsEmpty && double.TryParse(context.GetText(), NumberStyles.Any, + CultureInfo.InvariantCulture, out double d)) + return d; + return double.NaN; + } + } + + internal class InverseFlatteningVisitor : WktCrsBaseVisitor + { + public static readonly InverseFlatteningVisitor Instance = new InverseFlatteningVisitor(); + + public override double VisitInverseFlattening(WktCrsParser.InverseFlatteningContext context) + { + if (!context.IsEmpty && double.TryParse(context.GetText(), NumberStyles.Any, + CultureInfo.InvariantCulture, out double d)) + return d; + return double.NaN; + } + } + + + internal class SpheroidVisitor : WktCrsBaseVisitor + { + public static readonly SpheroidVisitor Instance = new SpheroidVisitor(); + + public override Ellipsoid VisitSpheroid(WktCrsParser.SpheroidContext context) + { + string name = string.Empty; + var nameCtx = context.name(); + if (nameCtx != null) + { + var visitor = NameVisitor.Instance; + name = visitor.VisitName(nameCtx); + } + + double semiMajorAxis = double.NaN; + var smaCtx = context.semiMajorAxis(); + if (smaCtx != null) + { + var visitor = new SemiMajorAxisVisitor(); + semiMajorAxis = visitor.VisitSemiMajorAxis(smaCtx); + } + + double inverseFlattening = double.NaN; + var invfCtx = context.inverseFlattening(); + if (invfCtx != null) + { + var visitor = new InverseFlatteningVisitor(); + inverseFlattening = visitor.VisitInverseFlattening(invfCtx); + } + + Authority authority = null; + var authCtx = context.authority(); + if (authCtx != null) + { + var visitor = AuthorityVisitor.Instance; + authority = visitor.Visit(authCtx); + } + + return new Ellipsoid(semiMajorAxis, 0.0, inverseFlattening, true, LinearUnit.Metre, name, + authority?.Name, authority != null ? authority.Code : -1, string.Empty, string.Empty, string.Empty); + + } + } + + + internal class ConversionFactorVisitor : WktCrsBaseVisitor + { + public override double VisitConversionFactor(WktCrsParser.ConversionFactorContext context) + { + if (!context.IsEmpty && double.TryParse(context.GetText(), NumberStyles.Any, + CultureInfo.InvariantCulture, out double d)) + return d; + return double.NaN; + } + } + + + internal class DatumVisitor : WktCrsBaseVisitor + { + public static readonly DatumVisitor Instance = new DatumVisitor(); + + private readonly CoordinateSystemFactory factory = new CoordinateSystemFactory(); + + public override HorizontalDatum VisitDatum(WktCrsParser.DatumContext context) + { + string name = string.Empty; + var nameCtx = context.name(); + if (nameCtx != null) + { + var visitor = NameVisitor.Instance; + name = visitor.VisitName(nameCtx); + } + + Ellipsoid spheroid = null; + var spheroidCtx = context.spheroid(); + if (spheroidCtx != null) + { + var visitor = SpheroidVisitor.Instance; + spheroid = visitor.VisitSpheroid(spheroidCtx); + } + + Wgs84ConversionInfo wgs84 = null; + var towgs84Ctx = context.towgs84(); + if (towgs84Ctx != null && towgs84Ctx.Length>0) + { + var visitor = new ToWgs84Visitor(); + wgs84 = visitor.VisitTowgs84(towgs84Ctx[0]); + } + + Authority authority = null; + var authorityCtx = context.authority(); + if (authorityCtx != null && authorityCtx.Length>0) + { + var visitor = AuthorityVisitor.Instance; + authority = visitor.VisitAuthority(authorityCtx[0]); + } + + var result = this.factory.CreateHorizontalDatum( + name, DatumType.HD_Geocentric, ellipsoid: spheroid, toWgs84: wgs84); + + if (authority != null) + { + result.Authority = authority.Name; + result.AuthorityCode = authority.Code; + } + + return result; + } + } + + internal class UnitVisitor : WktCrsBaseVisitor + { + public override Unit VisitUnit(WktCrsParser.UnitContext context) + { + string name = string.Empty; + var nameCtx = context.name(); + if (nameCtx != null) + { + var visitor = NameVisitor.Instance; + name = visitor.VisitName(nameCtx); + } + + double factor = double.NaN; + var cfCtx = context.conversionFactor(); + if (cfCtx != null) + { + var visitor = new ConversionFactorVisitor(); + factor = visitor.VisitConversionFactor(cfCtx); + } + + Authority authority = null; + var authorityCtx = context.authority(); + if (authorityCtx != null) + { + var visitor = AuthorityVisitor.Instance; + authority = visitor.VisitAuthority(authorityCtx); + } + + return new Unit(factor, name, authority?.Name, authority != null ? authority.Code : -1, string.Empty, + string.Empty, string.Empty); + } + } + + internal class PrimemVisitor : WktCrsBaseVisitor + { + public static readonly PrimemVisitor Instance = new PrimemVisitor(); + + private readonly CoordinateSystemFactory factory = new CoordinateSystemFactory(); + + public override PrimeMeridian VisitPrimem(WktCrsParser.PrimemContext context) + { + string name = string.Empty; + var nameCtx = context.name(); + if (nameCtx != null) + { + var visitor = NameVisitor.Instance; + name = visitor.VisitName(nameCtx); + } + + double longitude = double.NaN; + var ltCtx = context.longitude(); + if (ltCtx != null) + { + var visitor = new LongitudeVisitor(); + longitude = visitor.VisitLongitude(ltCtx); + } + + var au = AngularUnit.Degrees; + var unitCtx = context.unit(); + if (unitCtx != null) + { + var visitor = new UnitVisitor(); + var unit = visitor.VisitUnit(unitCtx); + if (unit != null && unit.Name.Equals("degree")) + { + au = new AngularUnit(unit.ConversionFactor, unit.Name, unit.Authority, unit.AuthorityCode, + string.Empty, string.Empty, string.Empty); + } + } + + Authority authority = null; + var authorityCtx = context.authority(); + if (authorityCtx != null) + { + var visitor = AuthorityVisitor.Instance; + authority = visitor.VisitAuthority(authorityCtx); + } + + var result = this.factory.CreatePrimeMeridian(name, angularUnit: au, longitude: longitude); + + if (authority is Authority authObj) + { + result.AuthorityCode = authObj.Code; + result.Authority = authObj.Name; + } + + return result; + } + } + + + internal class GeographicCoordinateSystemVisitor : WktCrsBaseVisitor + { + public static readonly GeographicCoordinateSystemVisitor Instance = new GeographicCoordinateSystemVisitor(); + + private readonly CoordinateSystemFactory factory = new CoordinateSystemFactory(); + + public override GeographicCoordinateSystem VisitGeogcs(WktCrsParser.GeogcsContext context) + { + string name = string.Empty; + var nameCtx = context.name(); + if (nameCtx != null) + { + var visitor = NameVisitor.Instance; + name = visitor.VisitName(nameCtx); + } + + var au = AngularUnit.Degrees; + var unitCtx = context.unit(); + if (unitCtx != null) + { + var visitor = new UnitVisitor(); + var unit = visitor.VisitUnit(unitCtx); + if (unit != null && unit.Name.Equals("degree")) + { + au = new AngularUnit(unit.ConversionFactor, unit.Name, unit.Authority, unit.AuthorityCode, + string.Empty, string.Empty, string.Empty); + } + } + + HorizontalDatum datum = null; + var datumCtx = context.datum(); + if (datumCtx != null) + { + var visitor = DatumVisitor.Instance; + datum = visitor.VisitDatum(datumCtx); + } + + PrimeMeridian pm = null; + var pmCtx = context.primem(); + if (pmCtx != null) + { + var visitor = PrimemVisitor.Instance; + pm = visitor.VisitPrimem(pmCtx); + } + + //This is default axis values if not specified. + var axVisitor = new AxisVisitor(); + var axisCtx = context.axis(); + var ax1 = axisCtx.Length > 0 + ? axVisitor.VisitAxis(axisCtx[0]) + : new AxisInfo("Lon", AxisOrientationEnum.East); + var ax2 = axisCtx.Length > 1 + ? axVisitor.VisitAxis(axisCtx[1]) + : new AxisInfo("Lat", AxisOrientationEnum.North); + + Authority authority = null; + var authorityCtx = context.authority(); + if (authorityCtx != null) + { + var visitor = AuthorityVisitor.Instance; + authority = visitor.VisitAuthority(authorityCtx); + } + + var result = this.factory.CreateGeographicCoordinateSystem(name, au, (HorizontalDatum) datum, + (PrimeMeridian) pm, axis0: ax1, axis1: ax2); + + if (authority is Authority authObj) + { + result.AuthorityCode = authObj.Code; + result.Authority = authObj.Name; + } + + return result; + } + } + + internal class GeocentricCoordinateSystemVisitor : WktCrsBaseVisitor + { + public static readonly GeocentricCoordinateSystemVisitor Instance = new GeocentricCoordinateSystemVisitor(); + + private readonly CoordinateSystemFactory factory = new CoordinateSystemFactory(); + + public override GeocentricCoordinateSystem VisitGeoccs(WktCrsParser.GeoccsContext context) + { + string name = string.Empty; + var nameCtx = context.name(); + if (nameCtx != null) + { + var visitor = NameVisitor.Instance; + name = visitor.VisitName(nameCtx); + } + + HorizontalDatum datum = null; + var datumCtx = context.datum(); + if (datumCtx != null) + { + var visitor = DatumVisitor.Instance; + datum = visitor.VisitDatum(datumCtx); + } + + PrimeMeridian meridian = null; + var pmCtx = context.primem(); + if (pmCtx != null) + { + var visitor = PrimemVisitor.Instance; + meridian = visitor.VisitPrimem(pmCtx); + } + + var lu = (LinearUnit) null; + var unitCtx = context.unit(); + if (unitCtx != null) + { + var visitor = new UnitVisitor(); + var u = visitor.VisitUnit(unitCtx); + if (u != null) + { + lu = new LinearUnit(u.ConversionFactor, u.Name, u.Authority, u.AuthorityCode, string.Empty, + string.Empty, string.Empty); + } + } + + Authority authority = null; + var authorityCtx = context.authority(); + if (authorityCtx != null) + { + var visitor = AuthorityVisitor.Instance; + authority = visitor.VisitAuthority(authorityCtx); + } + + var result = + this.factory.CreateGeocentricCoordinateSystem(name, (HorizontalDatum) datum, lu, + (PrimeMeridian) meridian); + + if (authority is Authority authObj) + { + result.AuthorityCode = authObj.Code; + result.Authority = authObj.Name; + } + + return result; + } + } + + + internal class ProjectedCoordinateSystemVisitor : WktCrsBaseVisitor + { + public static readonly ProjectedCoordinateSystemVisitor Instance = new ProjectedCoordinateSystemVisitor(); + + public override ProjectedCoordinateSystem VisitProjcs(WktCrsParser.ProjcsContext context) + { + string name = string.Empty; + var nameCtx = context.name(); + if (nameCtx != null) + { + var visitor = NameVisitor.Instance; + name = visitor.VisitName(nameCtx); + } + + GeographicCoordinateSystem gcs = null; + var gcsCtx = context.geogcs(); + if (gcsCtx != null) + { + var visitor = GeographicCoordinateSystemVisitor.Instance; + gcs = visitor.VisitGeogcs(gcsCtx); + } + + var lu = (LinearUnit) null; + var unitCtx = context.unit(); + if (unitCtx != null) + { + var visitor = new UnitVisitor(); + var u = visitor.VisitUnit(unitCtx); + if (u != null) + { + lu = new LinearUnit(u.ConversionFactor, u.Name, u.Authority, u.AuthorityCode, string.Empty, + string.Empty, string.Empty); + } + } + + Projection p = null; + var projCtx = context.projection(); + if (projCtx != null) + { + var visitor = new ProjectionVisitor(); + p = visitor.VisitProjection(projCtx); + } + + + var axVisitor = new AxisVisitor(); + var axisCtx = context.axis(); + var ax1 = axisCtx.Length > 0 + ? axVisitor.VisitAxis(axisCtx[0]) + : new AxisInfo("X", AxisOrientationEnum.East); + var ax2 = axisCtx.Length > 1 + ? axVisitor.VisitAxis(axisCtx[1]) + : new AxisInfo("Y", AxisOrientationEnum.North); + var aa = new List {ax1, ax2}; + + Authority authority = null; + var authorityCtx = context.authority(); + if (authorityCtx != null) + { + var visitor = AuthorityVisitor.Instance; + authority = visitor.VisitAuthority(authorityCtx); + } + + var result = new ProjectedCoordinateSystem(gcs.HorizontalDatum, gcs, lu, p, + aa, name, authority?.Name, authority != null ? authority.Code : -1, string.Empty, string.Empty, + string.Empty); + + return result; + } + } + + internal class ParamsMathTransformVisitor : WktCrsBaseVisitor + { + public static readonly ParamsMathTransformVisitor Instance = new ParamsMathTransformVisitor(); + + public override AffineTransform VisitParamsmt(WktCrsParser.ParamsmtContext context) + { + string name = ""; + var nameCtx = context.name(); + if (nameCtx != null) + { + var visitor = NameVisitor.Instance; + name = visitor.VisitName(nameCtx); + } + + var parameters = new List(); + var paramCtx = context.parameter(); + if (paramCtx != null) + { + var visitor = new ParameterVisitor(); + parameters = paramCtx.Select(pc => visitor.VisitParameter(pc)).ToList(); + } + + if (name.Equals("Affine", StringComparison.InvariantCultureIgnoreCase) && parameters.Any()) + { + /* + PARAM_MT[ + "Affine", + PARAMETER["num_row",3], + PARAMETER["num_col",3], + PARAMETER["elt_0_0", 0.883485346527455], + PARAMETER["elt_0_1", -0.468458794848877], + PARAMETER["elt_0_2", 3455869.17937689], + PARAMETER["elt_1_0", 0.468458794848877], + PARAMETER["elt_1_1", 0.883485346527455], + PARAMETER["elt_1_2", 5478710.88035753], + PARAMETER["elt_2_2", 1] + ] + */ + + + var p = parameters; + var rowParam = p.FirstOrDefault(x => x.Name == "num_row"); + var colParam = p.FirstOrDefault(x => x.Name == "num_col"); + + if (rowParam == null) + { + throw new ArgumentNullException(nameof(rowParam), + "Affine transform does not contain 'num_row' parameter"); + } + + if (colParam == null) + { + throw new ArgumentNullException(nameof(colParam), + "Affine transform does not contain 'num_col' parameter"); + } + + int rowVal = (int) rowParam.Value; + int colVal = (int) colParam.Value; + + if (rowVal <= 0) + { + throw new ArgumentException("Affine transform contains invalid value of 'num_row' parameter"); + } + + if (colVal <= 0) + { + throw new ArgumentException("Affine transform contains invalid value of 'num_col' parameter"); + } + + //creates working matrix; + double[,] matrix = new double[rowVal, colVal]; + + //simply process matrix values - no elt_ROW_COL parsing + foreach (var param in p) + { + if (param == null || param.Name == null) + { + continue; + } + + switch (param.Name) + { + case "num_row": + case "num_col": + break; + case "elt_0_0": + matrix[0, 0] = param.Value; + break; + case "elt_0_1": + matrix[0, 1] = param.Value; + break; + case "elt_0_2": + matrix[0, 2] = param.Value; + break; + case "elt_0_3": + matrix[0, 3] = param.Value; + break; + case "elt_1_0": + matrix[1, 0] = param.Value; + break; + case "elt_1_1": + matrix[1, 1] = param.Value; + break; + case "elt_1_2": + matrix[1, 2] = param.Value; + break; + case "elt_1_3": + matrix[1, 3] = param.Value; + break; + case "elt_2_0": + matrix[2, 0] = param.Value; + break; + case "elt_2_1": + matrix[2, 1] = param.Value; + break; + case "elt_2_2": + matrix[2, 2] = param.Value; + break; + case "elt_2_3": + matrix[2, 3] = param.Value; + break; + case "elt_3_0": + matrix[3, 0] = param.Value; + break; + case "elt_3_1": + matrix[3, 1] = param.Value; + break; + case "elt_3_2": + matrix[3, 2] = param.Value; + break; + case "elt_3_3": + matrix[3, 3] = param.Value; + break; + } + } + + //use "matrix" constructor to create transformation matrix + return new AffineTransform(matrix); + } + + return null; + } + } + + + internal class FittedCoordinateSystemVisitor : WktCrsBaseVisitor + { + public static readonly FittedCoordinateSystemVisitor Instance = new FittedCoordinateSystemVisitor(); + + public override FittedCoordinateSystem VisitFittedcs(WktCrsParser.FittedcsContext context) + { + string name = string.Empty; + var nameCtx = context.name(); + if (nameCtx != null) + { + var visitor = NameVisitor.Instance; + name = visitor.VisitName(nameCtx); + } + + MathTransform mathTransform = null; + var pmtCtx = context.paramsmt(); + if (pmtCtx != null) + { + var visitor = new ParamsMathTransformVisitor(); + mathTransform = visitor.VisitParamsmt(pmtCtx); + } + + ProjectedCoordinateSystem baseCS = null; + var projcsCtx = context.projcs(); + if (projcsCtx != null) + { + var visitor = new ProjectedCoordinateSystemVisitor(); + baseCS = visitor.VisitProjcs(projcsCtx); + } + + Authority authority = null; + var authorityCtx = context.authority(); + if (authorityCtx != null) + { + var visitor = AuthorityVisitor.Instance; + authority = visitor.VisitAuthority(authorityCtx); + } + + var result = new FittedCoordinateSystem(baseCS, mathTransform, name, authority?.Name, + authority != null ? authority.Code : -1, string.Empty, string.Empty, string.Empty); + + return result; + } + } + + + internal class WktCrsVisitor : WktCrsBaseVisitor + { + public static readonly WktCrsVisitor Instance = new WktCrsVisitor(); + + public override CoordinateSystem VisitWkt(WktCrsParser.WktContext context) + { + var projcsCtx = context.projcs(); + if (projcsCtx != null) + { + var visitor = ProjectedCoordinateSystemVisitor.Instance; + return visitor.VisitProjcs(projcsCtx); + } + + var gcsCtx = context.geogcs(); + if (gcsCtx != null) + { + var visitor = GeographicCoordinateSystemVisitor.Instance; + return visitor.VisitGeogcs(gcsCtx); + } + + var ccsCtx = context.geoccs(); + if (ccsCtx != null) + { + var visitor = GeocentricCoordinateSystemVisitor.Instance; + return visitor.VisitGeoccs(ccsCtx); + } + + //if (context.compdcs() != null) + //return (CoordinateSystem) Visit(context.compdcs()); + var fcsCtx = context.fittedcs(); + if (fcsCtx != null) + { + var visitor = FittedCoordinateSystemVisitor.Instance; + return visitor.VisitFittedcs(fcsCtx); + } + /* + else if (context.localcs()!=null) + return (CoordinateSystem) Visit(context.localcs()); + else if (context.vertcs()!=null) + return (CoordinateSystem) Visit(context.vertcs()); + */ + + return base.VisitWkt(context); + } + } + + private static WktCrsLexer cachedLexer = null; + private static WktCrsParser cachedParser = null; + + + /// + /// ParseAndBuild + /// + /// + /// + public static CoordinateSystem ParseAndBuild(string input) + { + try + { + var stream = CharStreams.fromString(input); + var lexer = cachedLexer; + if (lexer == null) + { + lexer = new WktCrsLexer(stream); + cachedLexer = lexer; + } + else + { + lexer.SetInputStream(stream); + } + + var tokens = new CommonTokenStream(lexer); + + var parser = cachedParser; + if (parser == null) + { + parser = new WktCrsParser(tokens); + parser.BuildParseTree = true; + cachedParser = parser; + } + else + { + //parser.BuildParseTree = false; + parser.TokenStream = tokens; + } + + parser.Interpreter.PredictionMode = Antlr4.Runtime.Atn.PredictionMode.SLL; + + bool doProfile = false; + parser.Profile = doProfile; + + var wktCtx = parser.wkt(); + if (wktCtx != null) + { + var result = WktCrsVisitor.Instance.VisitWkt(wktCtx); + + if (doProfile) + { + Console.WriteLine("Profile results: \n" + GetProfileInfo(parser)); + } + + return result; + } + } + catch (RecognitionException) + { + return null; + } + catch (ParseCanceledException) + { + return null; + } + return null; + } + + + private static string GetProfileInfo(WktCrsParser parser) + { + var sb = new StringBuilder(); + sb.AppendFormat("{0,-35}", "rule"); + sb.AppendFormat("{0,-15}", "time"); + sb.AppendFormat("{0,-15}", "invocations"); + sb.AppendFormat("{0,-15}", "lookahead"); + sb.AppendFormat("{0,-15}", "lookahead(max)"); + sb.AppendFormat("{0,-15}", "ambiguities"); + sb.AppendFormat("{0,-15}", "errors"); + sb.AppendLine(); + foreach (var decisionInfo in parser.ParseInfo.getDecisionInfo()) + { + var ds = parser.Atn.GetDecisionState(decisionInfo.decision); + string rule = parser.RuleNames[ds.ruleIndex]; + if (decisionInfo.timeInPrediction > 0) + { + sb.AppendFormat("{0,-35}", rule); + sb.AppendFormat("{0,-15}", decisionInfo.timeInPrediction); + sb.AppendFormat("{0,-15}", decisionInfo.invocations); + sb.AppendFormat("{0,-15}", decisionInfo.SLL_TotalLook); + sb.AppendFormat("{0,-15}", decisionInfo.SLL_MaxLook); + sb.AppendFormat("{0,-15}", decisionInfo.ambiguities.Count); + sb.AppendFormat("{0,-15}", decisionInfo.errors.Count); + sb.AppendLine(); + } + } + + return sb.ToString(); + } + + } +} diff --git a/src/ProjNet/ProjNET.csproj b/src/ProjNet/ProjNET.csproj index e5cfcda..442da61 100644 --- a/src/ProjNet/ProjNET.csproj +++ b/src/ProjNet/ProjNET.csproj @@ -25,7 +25,19 @@ Proj.NET performs point-to-point coordinate conversions between geodetic coordin - + + false + true + false + ProjNet.IO.Wkt + true + + + + + + + diff --git a/test/ProjNet.Tests/WKT/WktToProjBuilderTests.cs b/test/ProjNet.Tests/WKT/WktToProjBuilderTests.cs new file mode 100644 index 0000000..f78cf18 --- /dev/null +++ b/test/ProjNet.Tests/WKT/WktToProjBuilderTests.cs @@ -0,0 +1,185 @@ +using System; +using NUnit.Framework; +using ProjNet.CoordinateSystems; +using ProjNet.IO.Wkt; + +namespace ProjNET.Tests.WKT; + +public class WktToProjBuilderTests +{ + + private readonly CoordinateSystemFactory _coordinateSystemFactory = new CoordinateSystemFactory(); + + private readonly WktToProjBuilder _wktBuilder = new WktToProjBuilder(); + + + [Test] + public void TestProjectedCoordinateSystem_EPSG_2918() + { + const string wkt = "PROJCS[\"NAD83(HARN) / Texas Central (ftUS)\", "+ + "GEOGCS[\"NAD83(HARN)\", " + + "DATUM[\"NAD83_High_Accuracy_Regional_Network\", "+ + "SPHEROID[\"GRS 1980\", 6378137, 298.257222101, AUTHORITY[\"EPSG\", \"7019\"]], "+ + "TOWGS84[725, 685, 536, 0, 0, 0, 0], " + + "AUTHORITY[\"EPSG\", \"6152\"]], "+ + "PRIMEM[\"Greenwich\", 0, AUTHORITY[\"EPSG\", \"8901\"]], "+ + "UNIT[\"degree\", 0.0174532925199433, AUTHORITY[\"EPSG\", \"9122\"]], "+ + "AUTHORITY[\"EPSG\", \"4152\"]], "+ + "PROJECTION[\"Lambert_Conformal_Conic_2SP\"], " + + "PARAMETER[\"standard_parallel_1\", 31.883333333333], " + + "PARAMETER[\"standard_parallel_2\", 30.1166666667], " + + "PARAMETER[\"latitude_of_origin\", 29.6666666667], " + + "PARAMETER[\"central_meridian\", -100.333333333333], " + + "PARAMETER[\"false_easting\", 2296583.333], " + + "PARAMETER[\"false_northing\", 9842500], " + + "UNIT[\"US survey foot\", 0.304800609601219, AUTHORITY[\"EPSG\", \"9003\"]], "+ + "AUTHORITY[\"EPSG\", \"2918\"]]"; + + var cs = WktToProjBuilder.ParseAndBuild(wkt); + + Assert.IsNotNull(cs); + } + + + /// + /// This test reads in a file with 2671 pre-defined coordinate systems and projections, + /// and tries to parse them. + /// + [Test] + public void ParseAllWKTs() + { + int parseCount = 0; + foreach (var wkt in SRIDReader.GetSrids()) + { + 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"); + } + + /// + /// This test reads in a file with 2671 pre-defined coordinate systems and projections, + /// and tries to parse them. + /// + [Test] + public void ParseAllWKTs_ANTLR() + { + int parseCount = 0; + foreach (var wkt in SRIDReader.GetSrids()) + { + var cs1 = WktToProjBuilder.ParseAndBuild(wkt.Wkt); + Assert.IsNotNull(cs1, "Could not parse WKT: " + wkt.Wkt); + var cs2 = WktToProjBuilder.ParseAndBuild(wkt.Wkt.Replace("[", "(").Replace("]", ")")); + Assert.IsNotNull(cs2, "Could not parse WKT: " + wkt.Wkt); + Assert.That(cs1.EqualParams(cs2), Is.True); + parseCount++; + } + Assert.That(parseCount, Is.GreaterThan(2671), "Not all WKT was parsed"); + } + + + + [Test] + public void ParseProjCSWithExtension() + { + string wkt = "PROJCS[\"Google Maps Global Mercator\"," + + "GEOGCS[\"WGS 84\"," + + "DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563,AUTHORITY[\"EPSG\",\"7030\"]]," + + "AUTHORITY[\"EPSG\",\"6326\"]]," + + "PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]]," + + "UNIT[\"degree\",0.01745329251994328,AUTHORITY[\"EPSG\",\"9122\"]]," + + "AUTHORITY[\"EPSG\",\"4326\"]]," + + "PROJECTION[\"Mercator_2SP\"]," + + "PARAMETER[\"standard_parallel_1\",0]," + + "PARAMETER[\"latitude_of_origin\",0]," + + "PARAMETER[\"central_meridian\",0]," + + "PARAMETER[\"false_easting\",0]," + + "PARAMETER[\"false_northing\",0]," + + "UNIT[\"Meter\",1]," + + "EXTENSION[\"PROJ4\",\"+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs\"]," + + "AUTHORITY[\"EPSG\",\"900913\"]]"; + + var cs1 = WktToProjBuilder.ParseAndBuild(wkt); + + Assert.IsNotNull(cs1); + } + + + [Test] + public void ParseProjCSWithoutExtension() + { + string wkt = "PROJCS[\"Google Maps Global Mercator\"," + + "GEOGCS[\"WGS 84\"," + + "DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563,AUTHORITY[\"EPSG\",\"7030\"]]," + + "AUTHORITY[\"EPSG\",\"6326\"]]," + + "PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]]," + + "UNIT[\"degree\",0.01745329251994328,AUTHORITY[\"EPSG\",\"9122\"]]," + + "AUTHORITY[\"EPSG\",\"4326\"]]," + + "PROJECTION[\"Mercator_2SP\"]," + + "PARAMETER[\"standard_parallel_1\",0]," + + "PARAMETER[\"latitude_of_origin\",0]," + + "PARAMETER[\"central_meridian\",0]," + + "PARAMETER[\"false_easting\",0]," + + "PARAMETER[\"false_northing\",0]," + + "UNIT[\"Meter\",1]," + + "AUTHORITY[\"EPSG\",\"900913\"]]"; + + var cs1 = WktToProjBuilder.ParseAndBuild(wkt); + + Assert.IsNotNull(cs1); + } + + + [Test] + public void TestFittedCoordinateSystemWkt () + { + var fac = new CoordinateSystemFactory (); + FittedCoordinateSystem fcs = null; + string wkt = "FITTED_CS[\"Local coordinate system MNAU (based on Gauss-Krueger)\"," + + "PARAM_MT[\"Affine\"," + + "PARAMETER[\"num_row\",3],PARAMETER[\"num_col\",3],PARAMETER[\"elt_0_0\", 0.883485346527455],PARAMETER[\"elt_0_1\", -0.468458794848877],PARAMETER[\"elt_0_2\", 3455869.17937689],PARAMETER[\"elt_1_0\", 0.468458794848877],PARAMETER[\"elt_1_1\", 0.883485346527455],PARAMETER[\"elt_1_2\", 5478710.88035753],PARAMETER[\"elt_2_2\", 1]]," + + "PROJCS[\"DHDN / Gauss-Kruger zone 3\"," + + "GEOGCS[\"DHDN\"," + + "DATUM[\"Deutsches_Hauptdreiecksnetz\"," + + "SPHEROID[\"Bessel 1841\", 6377397.155, 299.1528128, AUTHORITY[\"EPSG\", \"7004\"]]," + + "TOWGS84[612.4, 77, 440.2, -0.054, 0.057, -2.797, 0.525975255930096]," + + "AUTHORITY[\"EPSG\", \"6314\"]]," + + "PRIMEM[\"Greenwich\", 0, AUTHORITY[\"EPSG\", \"8901\"]]," + + "UNIT[\"degree\", 0.0174532925199433, AUTHORITY[\"EPSG\", \"9122\"]]," + + "AUTHORITY[\"EPSG\", \"4314\"]]," + + "PROJECTION[\"Transverse_Mercator\"]," + + "PARAMETER[\"latitude_of_origin\", 0]," + + "PARAMETER[\"central_meridian\", 9]," + + "PARAMETER[\"scale_factor\", 1]," + + "PARAMETER[\"false_easting\", 3500000]," + + "PARAMETER[\"false_northing\", 0]," + + "UNIT[\"metre\", 1, AUTHORITY[\"EPSG\", \"9001\"]]," + + "AUTHORITY[\"EPSG\", \"31467\"]]" + + "]"; + + + try + { + //fcs = fac.CreateFromWkt (wkt) as FittedCoordinateSystem; + fcs = WktToProjBuilder.ParseAndBuild(wkt) as FittedCoordinateSystem; + } + catch (Exception ex) + { + Assert.Fail ("Could not create fitted coordinate system from:\r\n" + wkt + "\r\n" + ex.Message); + } + + Assert.That(fcs, Is.Not.Null); + Assert.That(fcs.ToBase(), Is.Not.Null.Or.Empty); + Assert.That(fcs.BaseCoordinateSystem, Is.Not.Null); + + Assert.AreEqual ("Local coordinate system MNAU (based on Gauss-Krueger)", fcs.Name); + //Assert.AreEqual ("CUSTOM", fcs.Authority); + //Assert.AreEqual (123456, fcs.AuthorityCode); + + Assert.AreEqual ("EPSG", fcs.BaseCoordinateSystem.Authority); + Assert.AreEqual (31467, fcs.BaseCoordinateSystem.AuthorityCode); + } +}