Skip to content

Commit 5821992

Browse files
authored
Support of the Auxiliary Mercator Sphere projection. (#127)
* Support of the Auxiliary Mercator Sphere projection. * Fixed unsupported c# 8.0 feature. * Changed WKT to a not corrupted format
1 parent 412f235 commit 5821992

File tree

3 files changed

+160
-16
lines changed

3 files changed

+160
-16
lines changed
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: 16 additions & 16 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
{
@@ -21,25 +20,26 @@ public class ProjectionsRegistry
2120
static ProjectionsRegistry()
2221
{
2322
Register("mercator", typeof(Mercator));
24-
Register("mercator_1sp", typeof (Mercator));
25-
Register("mercator_2sp", typeof (Mercator));
23+
Register("mercator_1sp", typeof(Mercator));
24+
Register("mercator_2sp", typeof(Mercator));
25+
Register("mercator_auxiliary_sphere", typeof(MercatorAuxiliarySphere));
2626
Register("pseudo-mercator", typeof(PseudoMercator));
2727
Register("popular_visualisation pseudo-mercator", typeof(PseudoMercator));
28-
Register("google_mercator", typeof(PseudoMercator));
29-
28+
Register("google_mercator", typeof(PseudoMercator));
29+
3030
Register("transverse_mercator", typeof(TransverseMercator));
3131
Register("gauss_kruger", typeof(TransverseMercator));
3232

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));
39-
40-
Register("lambert_conformal_conic", typeof(LambertConformalConic2SP));
41-
Register("lambert_conformal_conic_2sp", typeof(LambertConformalConic2SP));
42-
Register("lambert_conic_conformal_(2sp)", typeof(LambertConformalConic2SP));
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));
39+
40+
Register("lambert_conformal_conic", typeof(LambertConformalConic2SP));
41+
Register("lambert_conformal_conic_2sp", typeof(LambertConformalConic2SP));
42+
Register("lambert_conic_conformal_(2sp)", typeof(LambertConformalConic2SP));
4343

4444
Register("lambert_azimuthal_equal_area", typeof(LambertAzimuthalEqualAreaProjection));
4545

test/ProjNet.Tests/CoordinateTransformTests.cs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1128,5 +1128,39 @@ public void TestEllipsoidalOrthographicTransform()
11281128
var @delegate2 = new TestDelegate(() => trans2.MathTransform.Transform(new[] { 180.0, 0.0 }));
11291129
Assert.Throws<ArgumentOutOfRangeException>(@delegate2);
11301130
}
1131+
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+
1153+
internal static CoordinateSystem GetCoordinateSystem(string wkt)
1154+
{
1155+
var coordinateSystemFactory = new CoordinateSystemFactory();
1156+
return coordinateSystemFactory.CreateFromWkt(wkt);
1157+
}
1158+
1159+
internal static ICoordinateTransformation GetTransformation(CoordinateSystem sourceCoordinateSystem, CoordinateSystem targetCoordinateSystem)
1160+
{
1161+
var coordinateSystemFactory = new CoordinateSystemFactory();
1162+
var coordinateService = new ProjNet.CoordinateSystemServices(coordinateSystemFactory, new CoordinateTransformationFactory());
1163+
return coordinateService.CreateTransformation(sourceCoordinateSystem, targetCoordinateSystem);
1164+
}
11311165
}
11321166
}

0 commit comments

Comments
 (0)