Skip to content

Commit 7b4518f

Browse files
committed
Merge develop into branch
2 parents 7a67bdc + 3767f22 commit 7b4518f

6 files changed

Lines changed: 191 additions & 23 deletions

File tree

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
using ProjNet.CoordinateSystems.Transformations;
2+
using System;
3+
using System.Collections.Generic;
4+
5+
namespace ProjNet.CoordinateSystems.Projections
6+
{
7+
/// <summary>
8+
/// Implements the Mercator Auxiliary Sphere projection (Web Mercator).
9+
/// This projection uses a spherical model with a constant radius.
10+
/// </summary>
11+
[Serializable]
12+
internal class MercatorAuxiliarySphere : MapProjection
13+
{
14+
// Scale factor – for the spherical (auxiliary) Mercator this is 1.
15+
private const double _k0 = 1.0;
16+
17+
/// <summary>
18+
/// Initializes the MercatorAuxiliarySphere projection with the specified parameters.
19+
/// </summary>
20+
/// <param name="parameters">List of projection parameters.</param>
21+
public MercatorAuxiliarySphere(IEnumerable<ProjectionParameter> parameters)
22+
: this(parameters, null)
23+
{
24+
}
25+
26+
/// <summary>
27+
/// Initializes the MercatorAuxiliarySphere projection with the specified parameters.
28+
/// </summary>
29+
/// <param name="parameters">List of projection parameters.</param>
30+
/// <param name="isInverse">Reference to the inverse projection.</param>
31+
protected MercatorAuxiliarySphere(IEnumerable<ProjectionParameter> parameters, MercatorAuxiliarySphere isInverse)
32+
: base(parameters, isInverse)
33+
{
34+
Authority = "EPSG";
35+
Name = "Mercator_Auxiliary_Sphere";
36+
}
37+
38+
/// <summary>
39+
/// Converts geographic coordinates (in radians) to projected coordinates (in meters).
40+
/// </summary>
41+
/// <param name="lon">Longitude in radians.</param>
42+
/// <param name="lat">Latitude in radians.</param>
43+
/// <remarks>
44+
/// It is assumed that _semiMajor and central_meridian (as well as other parameters like false_easting/false_northing)
45+
/// are already set in the base class.
46+
/// </remarks>
47+
protected override void RadiansToMeters(ref double lon, ref double lat)
48+
{
49+
if (double.IsNaN(lon) || double.IsNaN(lat))
50+
{
51+
lon = double.NaN;
52+
lat = double.NaN;
53+
return;
54+
}
55+
56+
double dLon = lon;
57+
double dLat = lat;
58+
59+
if (Math.Abs(Math.Abs(dLat) - HALF_PI) <= EPSLN)
60+
{
61+
throw new ArgumentException("Transformation cannot be computed at the poles.");
62+
}
63+
64+
// Forward equations for the Spherical (Auxiliary) Mercator Projection:
65+
// X = semiMajor * k0 * (lon - central_meridian)
66+
// Y = semiMajor * k0 * ln( tan(PI/4 + lat/2) )
67+
lon = _semiMajor * _k0 * (dLon - central_meridian);
68+
lat = _semiMajor * _k0 * Math.Log(Math.Tan((PI * 0.25) + (dLat * 0.5)));
69+
// Note: false_easting and false_northing can be added here if necessary.
70+
}
71+
72+
/// <summary>
73+
/// Converts projected coordinates (in meters) to geographic coordinates (in radians).
74+
/// </summary>
75+
/// <param name="x">X coordinate in meters.</param>
76+
/// <param name="y">Y coordinate in meters.</param>
77+
/// <remarks>
78+
/// Uses the inverse transformation of the Spherical Mercator Projection.
79+
/// </remarks>
80+
protected override void MetersToRadians(ref double x, ref double y)
81+
{
82+
double dX = x;
83+
double dY = y;
84+
85+
// Inverse equations:
86+
// lon = central_meridian + X / (semiMajor * k0)
87+
// lat = PI/2 - 2 * atan( exp( -Y / (semiMajor * k0) ) )
88+
double ts = Math.Exp(-dY / (_semiMajor * _k0));
89+
double dLat = HALF_PI - (2 * Math.Atan(ts));
90+
double dLon = central_meridian + (dX / (_semiMajor * _k0));
91+
92+
x = dLon;
93+
y = dLat;
94+
// Note: false_easting/false_northing can be subtracted here if provided in the parameter list.
95+
}
96+
97+
/// <summary>
98+
/// Returns the inverse transformation of this projection.
99+
/// </summary>
100+
/// <returns>The inverse projection as MathTransform.</returns>
101+
public override MathTransform Inverse()
102+
{
103+
if (_inverse is null)
104+
{
105+
_inverse = new MercatorAuxiliarySphere(_Parameters.ToProjectionParameter(), this);
106+
}
107+
return _inverse;
108+
}
109+
}
110+
}

src/ProjNet/CoordinateSystems/Projections/ProjectionsRegistry.cs

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1+
using ProjNet.CoordinateSystems.Transformations;
12
using System;
23
using System.Collections.Generic;
3-
using System.Reflection;
4-
using ProjNet.CoordinateSystems.Transformations;
54

65
namespace ProjNet.CoordinateSystems.Projections
76
{
@@ -26,21 +25,21 @@ static ProjectionsRegistry()
2625
Register("mercator_auxiliary_sphere", typeof(MercatorAuxiliarySphere));
2726
Register("pseudo_mercator", typeof(PseudoMercator));
2827
Register("popular_visualisation_pseudo_mercator", typeof(PseudoMercator));
29-
Register("google_mercator", typeof(PseudoMercator));
30-
28+
Register("google_mercator", typeof(PseudoMercator));
29+
3130
Register("transverse_mercator", typeof(TransverseMercator));
3231
Register("gauss_kruger", typeof(TransverseMercator));
3332

34-
Register("albers", typeof(AlbersProjection));
35-
Register("albers_conic_equal_area", typeof(AlbersProjection));
36-
37-
Register("krovak", typeof(KrovakProjection));
33+
Register("albers", typeof(AlbersProjection));
34+
Register("albers_conic_equal_area", typeof(AlbersProjection));
35+
36+
Register("krovak", typeof(KrovakProjection));
37+
38+
Register("polyconic", typeof(PolyconicProjection));
3839

39-
Register("polyconic", typeof(PolyconicProjection));
40-
41-
Register("lambert_conformal_conic", typeof(LambertConformalConic2SP));
42-
Register("lambert_conformal_conic_2sp", typeof(LambertConformalConic2SP));
43-
Register("lambert_conic_conformal_(2sp)", typeof(LambertConformalConic2SP));
40+
Register("lambert_conformal_conic", typeof(LambertConformalConic2SP));
41+
Register("lambert_conformal_conic_2sp", typeof(LambertConformalConic2SP));
42+
Register("lambert_conic_conformal_(2sp)", typeof(LambertConformalConic2SP));
4443

4544
Register("lambert_azimuthal_equal_area", typeof(LambertAzimuthalEqualAreaProjection));
4645

src/ProjNet/IO/CoordinateSystems/CoordinateSystemWktReader.cs

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
using System;
3939
using System.Collections.Generic;
4040
using System.IO;
41+
using System.Linq;
4142
using System.Runtime.InteropServices.ComTypes;
4243
using System.Text;
4344
using ProjNet.CoordinateSystems;
@@ -360,14 +361,22 @@ private static ProjectedCoordinateSystem ReadProjectedCoordinateSystem(WktStream
360361
tokenizer.ReadToken("GEOGCS");
361362
var geographicCS = ReadGeographicCoordinateSystem(tokenizer);
362363
tokenizer.ReadToken(",");
363-
tokenizer.ReadToken("PROJECTION");
364+
tokenizer.NextToken();
365+
366+
LinearUnit linearUnit = null;
367+
368+
if (tokenizer.GetStringValue().Equals("UNIT", StringComparison.OrdinalIgnoreCase))
369+
{
370+
linearUnit = ReadLinearUnit(tokenizer);
371+
tokenizer.ReadToken(",");
372+
}
364373
var projection = ReadProjection(tokenizer);
365-
var unit = ReadLinearUnit(tokenizer);
374+
var unit = linearUnit ?? ReadLinearUnit(tokenizer);
366375
var axisInfo = new List<AxisInfo>(2);
367376
string authority = string.Empty;
368377
long authorityCode = -1;
369378

370-
tokenizer.NextToken();
379+
var ct = tokenizer.NextToken();
371380
if (tokenizer.GetStringValue() == ",")
372381
{
373382
tokenizer.NextToken();
@@ -377,11 +386,18 @@ private static ProjectedCoordinateSystem ReadProjectedCoordinateSystem(WktStream
377386
tokenizer.NextToken();
378387
if (tokenizer.GetStringValue() == ",") tokenizer.NextToken();
379388
}
380-
if (tokenizer.GetStringValue() == ",") tokenizer.NextToken();
381-
if (tokenizer.GetStringValue() == "AUTHORITY")
389+
390+
while (ct != TokenType.Eol && ct != TokenType.Eof)
382391
{
383-
tokenizer.ReadAuthority(out authority, out authorityCode);
384-
tokenizer.ReadCloser(bracket);
392+
if (tokenizer.GetStringValue() == "AUTHORITY")
393+
{
394+
tokenizer.ReadAuthority(out authority, out authorityCode);
395+
break;
396+
}
397+
else
398+
{
399+
ct = tokenizer.NextToken();
400+
}
385401
}
386402
}
387403
//This is default axis values if not specified.
@@ -443,8 +459,13 @@ private static CompoundCoordinateSystem ReadCompoundCoordinateSystem(WktStreamTo
443459
tokenizer.ReadToken(",");
444460
tokenizer.NextToken();
445461
var headcs = ReadCoordinateSystem(null, tokenizer);
446-
tokenizer.ReadToken(",");
447-
tokenizer.NextToken();
462+
463+
var ct = tokenizer.NextToken();
464+
while (ct != TokenType.Eol && ct != TokenType.Eof && new[] { ",", "]"}.Contains(tokenizer.GetStringValue()))
465+
{
466+
ct = tokenizer.NextToken();
467+
468+
}
448469
var tailcs = ReadCoordinateSystem(null, tokenizer);
449470

450471
string authority = string.Empty;

test/ProjNet.Tests/CoordinateSystemServicesTest.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ private static IEnumerable<KeyValuePair<int, string>> LoadXml(string xmlPath)
8888
var sridElement = node.Element("SRID");
8989
if (sridElement != null)
9090
{
91-
var srid = int.Parse(sridElement.Value);
91+
int srid = int.Parse(sridElement.Value);
9292
yield return new KeyValuePair<int, string>(srid, node.LastNode.ToString());
9393
}
9494
}

test/ProjNet.Tests/CoordinateTransformTests.cs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1129,6 +1129,27 @@ public void TestEllipsoidalOrthographicTransform()
11291129
Assert.Throws<ArgumentOutOfRangeException>(@delegate2);
11301130
}
11311131

1132+
[Test]
1133+
public static void TestMercatorAuxilarySphereTransformation()
1134+
{
1135+
string sourceWkt = "PROJCS[\"WGS_1984_Web_Mercator_Auxiliary_Sphere\",GEOGCS[\"GCS_WGS_1984\",DATUM[\"D_WGS_1984\",SPHEROID[\"WGS_1984\",6378137.0,298.257223563]],PRIMEM[\"Greenwich\",0.0],UNIT[\"Degree\",0.0174532925199433]],PROJECTION[\"Mercator_Auxiliary_Sphere\"],PARAMETER[\"False_Easting\",0.0],PARAMETER[\"False_Northing\",0.0],PARAMETER[\"Central_Meridian\",0.0],PARAMETER[\"Standard_Parallel_1\",0.0],PARAMETER[\"Auxiliary_Sphere_Type\",0.0],UNIT[\"Meter\",1.0]]";
1136+
var sourceCoordinateSystem = GetCoordinateSystem(sourceWkt);
1137+
Assert.NotNull(sourceCoordinateSystem);
1138+
1139+
string targetWkt = "PROJCS[\"TX83-NCF\",GEOGCS[\"LL83\",DATUM[\"NAD83\",SPHEROID[\"GRS1980\",6378137.000,298.25722210]],PRIMEM[\"Greenwich\",0],UNIT[\"Degree\",0.017453292519943295]],PROJECTION[\"Lambert_Conformal_Conic_2SP\"],PARAMETER[\"false_easting\",1968500.000],PARAMETER[\"false_northing\",6561666.667],PARAMETER[\"central_meridian\",-98.50000000000000],PARAMETER[\"latitude_of_origin\",31.66666666666666],PARAMETER[\"standard_parallel_1\",33.96666666666667],PARAMETER[\"standard_parallel_2\",32.13333333333333],UNIT[\"Foot_US\",0.30480060960122]]";
1140+
var targetCoordinateSystem = GetCoordinateSystem(targetWkt);
1141+
Assert.NotNull(targetCoordinateSystem);
1142+
1143+
var transformation = GetTransformation(sourceCoordinateSystem, targetCoordinateSystem);
1144+
Assert.NotNull(transformation);
1145+
1146+
var tranformedPoint = transformation.MathTransform.Transform(-10775704.511, 3865240.329);
1147+
Assert.NotNull(tranformedPoint);
1148+
1149+
Assert.AreEqual(2491034.95, tranformedPoint.x, 0.1);
1150+
Assert.AreEqual(6968468.98, tranformedPoint.y, 0.1);
1151+
}
1152+
11321153
[Test]
11331154
public void TestPopularVisualizationPseudoMercatorProjectionRegistry()
11341155
{

test/ProjNet.Tests/ProjNetIssues.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,5 +297,22 @@ public void TestGitHubIssue98()
297297
Assert.IsTrue(cmpdCs.HeadCoordinateSystem is ProjectedCoordinateSystem);
298298
Assert.IsTrue(cmpdCs.TailCoordinateSystem is VerticalCoordinateSystem);
299299
}
300+
301+
/// <summary>
302+
/// Tests if a coordinate system can be created from a Well-Known Text (WKT) representation
303+
/// and verifies that the authority code is correctly loaded.
304+
/// </summary>
305+
/// <remarks>
306+
/// This test ensures that the WKT parsing functionality of the CoordinateSystemFactory
307+
/// correctly initializes the coordinate system and its associated metadata, such as the authority code.
308+
/// </remarks>
309+
[Test]
310+
public void TestAuthorityNotLoadedIssue()
311+
{
312+
string wkt = "PROJCS[\"WGS 84 / Pseudo-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.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[\"X\",EAST],AXIS[\"Y\",NORTH],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\",\"3857\"]]";
313+
var coordinateSystem = CoordinateSystemFactory.CreateFromWkt(wkt);
314+
Assert.IsNotNull(coordinateSystem);
315+
Assert.AreEqual(coordinateSystem.AuthorityCode, 3857);
316+
}
300317
}
301318
}

0 commit comments

Comments
 (0)