Skip to content

Commit 05a8099

Browse files
Add compound and vertical coordinate systems (#103)
* Add compound and vertical coordinate systems, as well as parsing WKT for these systems. * Add vertical datum * Modify tests that had been failing due to invalid WKT * Modify WKT of the ProjectedCoordinateSystem so that it follows the OGC specs. This is WIP and will be revisitied later.
1 parent ffa60cb commit 05a8099

File tree

11 files changed

+551
-81
lines changed

11 files changed

+551
-81
lines changed
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Globalization;
4+
using System.Text;
5+
6+
namespace ProjNet.CoordinateSystems
7+
{
8+
/// <summary>
9+
/// This is a compound coordinate system, which combines the coordinate of two other coordinate systems.
10+
/// For example, a compound 3D coordinate system could be made up of a
11+
/// horizontal coordinate system and a vertical coordinate system.
12+
/// </summary>
13+
public class CompoundCoordinateSystem : CoordinateSystem
14+
{
15+
private CoordinateSystem _headCoordinateSystem;
16+
private CoordinateSystem _tailCoordinateSystem;
17+
18+
/// <inheritdoc/>
19+
public override string WKT
20+
{
21+
get
22+
{
23+
var sb = new StringBuilder();
24+
sb.Append($"COMPD_CS[\"{Name}\",{HeadCoordinateSystem.WKT},{TailCoordinateSystem.WKT}");
25+
if (!string.IsNullOrWhiteSpace(Authority) && AuthorityCode > 0)
26+
{
27+
sb.Append($",AUTHORITY[\"{Authority}\",\"{AuthorityCode}\"]");
28+
}
29+
sb.Append("]");
30+
return sb.ToString();
31+
}
32+
}
33+
34+
/// <inheritdoc/>
35+
public override string XML
36+
{
37+
get
38+
{
39+
var sb = new StringBuilder();
40+
sb.AppendFormat(CultureInfo.InvariantCulture.NumberFormat,
41+
"<CS_CoordinateSystem Dimension=\"{0}\"><CS_CompoundCoordinateSystem>{1}",
42+
this.Dimension, InfoXml);
43+
foreach (var ai in AxisInfo)
44+
sb.Append(ai.XML);
45+
sb.Append(HeadCoordinateSystem.XML);
46+
sb.Append(TailCoordinateSystem.XML);
47+
sb.AppendFormat("</CS_CompoundCoordinateSystem></CS_CoordinateSystem>");
48+
return sb.ToString();
49+
}
50+
}
51+
52+
/// <summary>
53+
/// The head coordinate system
54+
/// </summary>
55+
public CoordinateSystem HeadCoordinateSystem { get => _headCoordinateSystem; set { _headCoordinateSystem = value; } }
56+
57+
/// <summary>
58+
/// The tail coordinate system
59+
/// </summary>
60+
public CoordinateSystem TailCoordinateSystem { get => _tailCoordinateSystem; set { _tailCoordinateSystem = value; } }
61+
/// <summary>
62+
/// A compound coordinate system
63+
/// </summary>
64+
/// <param name="headcs">The head (first) coordinate system</param>
65+
/// <param name="tailcs">The tail (second) coordinate system</param>
66+
/// <param name="name">Name</param>
67+
/// <param name="authority">Authority name</param>
68+
/// <param name="authorityCode">Authority-specific identification code</param>
69+
/// <param name="alias">Alias</param>
70+
/// <param name="abbreviation">Abbreviation</param>
71+
/// <param name="remarks">Optional information</param>
72+
public CompoundCoordinateSystem(CoordinateSystem headcs, CoordinateSystem tailcs, string name, string authority, long authorityCode, string alias, string abbreviation, string remarks)
73+
: base(name, authority, authorityCode, alias, abbreviation, remarks)
74+
{
75+
_headCoordinateSystem = headcs;
76+
_tailCoordinateSystem = tailcs;
77+
AxisInfo = new List<AxisInfo>();
78+
AxisInfo.AddRange(HeadCoordinateSystem.AxisInfo);
79+
AxisInfo.AddRange(TailCoordinateSystem.AxisInfo);
80+
}
81+
82+
/// <inheritdoc/>
83+
public override bool EqualParams(object obj)
84+
{
85+
if( obj is CompoundCoordinateSystem compdCs )
86+
{
87+
return HeadCoordinateSystem.EqualParams(compdCs.HeadCoordinateSystem) && TailCoordinateSystem.EqualParams(compdCs.TailCoordinateSystem);
88+
}
89+
90+
return false;
91+
}
92+
93+
/// <inheritdoc/>
94+
public override IUnit GetUnits(int dimension)
95+
{
96+
if( dimension < 0 || dimension >= Dimension )
97+
{
98+
throw new ArgumentException("Dimension not valid", nameof(dimension));
99+
}
100+
101+
if( dimension < HeadCoordinateSystem.Dimension)
102+
{
103+
return HeadCoordinateSystem.GetUnits(dimension);
104+
}
105+
106+
return TailCoordinateSystem.GetUnits(dimension - HeadCoordinateSystem.Dimension);
107+
}
108+
}
109+
}

src/ProjNet/CoordinateSystems/CoordinateSystemFactory.cs

Lines changed: 46 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ namespace ProjNet.CoordinateSystems
2626
/// Builds up complex objects from simpler objects or values.
2727
/// </summary>
2828
/// <remarks>
29-
/// <para>ICoordinateSystemFactory allows applications to make coordinate systems that
30-
/// cannot be created by a <see cref="ICoordinateSystemAuthorityFactory"/>. This factory is very
29+
/// <para>CoordinateSystemFactory allows applications to make coordinate systems that
30+
/// cannot be created by a <see cref="CoordinateSystemAuthorityFactory"/>. This factory is very
3131
/// flexible, whereas the authority factory is easier to use.</para>
3232
/// <para>So <see cref="ICoordinateSystemAuthorityFactory"/>can be used to make 'standard' coordinate
3333
/// systems, and <see cref="CoordinateSystemFactory"/> can be used to make 'special'
@@ -67,19 +67,22 @@ public CoordinateSystem CreateFromWkt(string WKT)
6767
return info as CoordinateSystem;
6868
}
6969

70-
/*
70+
7171
/// <summary>
7272
/// Creates a <see cref="CompoundCoordinateSystem"/> [NOT IMPLEMENTED].
7373
/// </summary>
7474
/// <param name="name">Name of compound coordinate system.</param>
7575
/// <param name="head">Head coordinate system</param>
7676
/// <param name="tail">Tail coordinate system</param>
7777
/// <returns>Compound coordinate system</returns>
78-
public ICompoundCoordinateSystem CreateCompoundCoordinateSystem(string name, ICoordinateSystem head, ICoordinateSystem tail)
78+
public CompoundCoordinateSystem CreateCompoundCoordinateSystem(string name, CoordinateSystem head, CoordinateSystem tail)
7979
{
80-
throw new NotImplementedException();
80+
if (string.IsNullOrWhiteSpace(name))
81+
throw new ArgumentException("Invalid name");
82+
83+
return new CompoundCoordinateSystem(head, tail, name, string.Empty, -1, string.Empty, string.Empty, string.Empty);
8184
}
82-
*/
85+
8386
/// <summary>
8487
/// Creates a <see cref="FittedCoordinateSystem"/>.
8588
/// </summary>
@@ -91,12 +94,30 @@ public ICompoundCoordinateSystem CreateCompoundCoordinateSystem(string name, ICo
9194
/// should not rotate this coordinate system in any other plane.</remarks>
9295
/// <param name="name">Name of coordinate system</param>
9396
/// <param name="baseCoordinateSystem">Base coordinate system</param>
94-
/// <param name="toBaseWkt"></param>
95-
/// <param name="arAxes"></param>
97+
/// <param name="toBaseWkt">WKT of the math transform to the base coordinate system</param>
98+
/// <param name="arAxes">Axiis of the fitted coordinate system</param>
9699
/// <returns>Fitted coordinate system</returns>
97100
public FittedCoordinateSystem CreateFittedCoordinateSystem(string name, CoordinateSystem baseCoordinateSystem, string toBaseWkt, List<AxisInfo> arAxes)
98101
{
99-
throw new NotImplementedException();
102+
if (string.IsNullOrWhiteSpace(name))
103+
throw new ArgumentException("Invalid name");
104+
105+
var toBaseTransform = MathTransformWktReader.Parse(toBaseWkt);
106+
return new FittedCoordinateSystem(baseCoordinateSystem, toBaseTransform, name, string.Empty, -1, string.Empty, string.Empty, string.Empty);
107+
}
108+
109+
/// <inheritdoc cref="CreateFittedCoordinateSystem(string, CoordinateSystem, string, List{AxisInfo})"/>
110+
/// <param name="name">Name of coordinate system</param>
111+
/// <param name="baseCoordinateSystem">Base coordinate system</param>
112+
/// <param name="toBase">the math transform to the base coordinate system</param>
113+
/// <param name="arAxes">Axiis of the fitted coordinate system</param>
114+
/// <returns></returns>
115+
public FittedCoordinateSystem CreateFittedCoordinateSystem(string name, CoordinateSystem baseCoordinateSystem, Transformations.MathTransform toBase, List<AxisInfo> arAxes)
116+
{
117+
if (string.IsNullOrWhiteSpace(name))
118+
throw new ArgumentException("Invalid name");
119+
120+
return new FittedCoordinateSystem(baseCoordinateSystem, toBase, name, string.Empty, -1, string.Empty, string.Empty, string.Empty);
100121
}
101122

102123
/*
@@ -270,33 +291,39 @@ public ILocalDatum CreateLocalDatum(string name, DatumType datumType)
270291
}
271292
*/
272293

273-
/*
294+
274295
/// <summary>
275-
/// Creates a <see cref="IVerticalDatum"/> from an enumerated type value.
296+
/// Creates a <see cref="VerticalDatum"/> from an enumerated type value.
276297
/// </summary>
277298
/// <param name="name">Name of datum</param>
278299
/// <param name="datumType">Type of datum</param>
279300
/// <returns>Vertical datum</returns>
280-
public IVerticalDatum CreateVerticalDatum(string name, DatumType datumType)
301+
public VerticalDatum CreateVerticalDatum(string name, DatumType datumType)
281302
{
282-
throw new NotImplementedException();
303+
if (string.IsNullOrWhiteSpace(name))
304+
throw new ArgumentException("Invalid name");
305+
306+
return new VerticalDatum(datumType, name, string.Empty, -1, string.Empty, string.Empty, string.Empty);
283307
}
284-
*/
285308

286-
/*
309+
310+
287311
/// <summary>
288-
/// Creates a <see cref="IVerticalCoordinateSystem"/> from a <see cref="IVerticalDatum">datum</see> and <see cref="LinearUnit">linear units</see>.
312+
/// Creates a <see cref="VerticalCoordinateSystem"/> from a <see cref="VerticalDatum">datum</see> and <see cref="LinearUnit">linear units</see>.
289313
/// </summary>
290314
/// <param name="name">Name of vertical coordinate system</param>
291315
/// <param name="datum">Vertical datum</param>
292316
/// <param name="verticalUnit">Unit</param>
293317
/// <param name="axis">Axis info</param>
294318
/// <returns>Vertical coordinate system</returns>
295-
public IVerticalCoordinateSystem CreateVerticalCoordinateSystem(string name, IVerticalDatum datum, ILinearUnit verticalUnit, AxisInfo axis)
319+
public VerticalCoordinateSystem CreateVerticalCoordinateSystem(string name, VerticalDatum datum, LinearUnit verticalUnit, AxisInfo axis)
296320
{
297-
throw new NotImplementedException();
321+
if (string.IsNullOrWhiteSpace(name))
322+
throw new ArgumentException("Invalid name");
323+
324+
return new VerticalCoordinateSystem(verticalUnit, datum, axis, name, string.Empty, -1, string.Empty, string.Empty, string.Empty);
298325
}
299-
*/
326+
300327
/// <summary>
301328
/// Creates a <see cref="CreateGeocentricCoordinateSystem"/> from a <see cref="HorizontalDatum">datum</see>,
302329
/// <see cref="LinearUnit">linear unit</see> and <see cref="PrimeMeridian"/>.

src/ProjNet/CoordinateSystems/ProjectedCoordinateSystem.cs

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -156,21 +156,19 @@ public override string WKT
156156
get
157157
{
158158
var sb = new StringBuilder();
159-
sb.AppendFormat("PROJCS[\"{0}\", {1}, {2}, {3}", Name, GeographicCoordinateSystem.WKT, LinearUnit.WKT, Projection.WKT);
159+
sb.AppendFormat("PROJCS[\"{0}\", {1}, {2}", Name, GeographicCoordinateSystem.WKT, Projection.WKT);
160160
for(int i=0;i<Projection.NumParameters;i++)
161161
sb.AppendFormat(CultureInfo.InvariantCulture.NumberFormat, ", {0}", Projection.GetParameter(i).WKT);
162-
//sb.AppendFormat(", {0}", LinearUnit.WKT);
163-
//Skip authority and code if not defined
164-
if (!string.IsNullOrWhiteSpace(Authority) && AuthorityCode > 0)
165-
sb.AppendFormat(", AUTHORITY[\"{0}\", \"{1}\"]", Authority, AuthorityCode);
162+
sb.AppendFormat(", {0}", LinearUnit.WKT);
166163
//Skip axis info if they contain default values
167164
if (AxisInfo.Count != 2 ||
168165
AxisInfo[0].Name != "X" || AxisInfo[0].Orientation != AxisOrientationEnum.East ||
169166
AxisInfo[1].Name != "Y" || AxisInfo[1].Orientation != AxisOrientationEnum.North)
170167
for (int i = 0; i < AxisInfo.Count; i++)
171168
sb.AppendFormat(", {0}", GetAxis(i).WKT);
172-
//if (!string.IsNullOrWhiteSpace(Authority) && AuthorityCode > 0)
173-
// sb.AppendFormat(", AUTHORITY[\"{0}\", \"{1}\"]", Authority, AuthorityCode);
169+
//Skip authority and code if not defined
170+
if (!string.IsNullOrWhiteSpace(Authority) && AuthorityCode > 0)
171+
sb.AppendFormat(", AUTHORITY[\"{0}\", \"{1}\"]", Authority, AuthorityCode);
174172
sb.Append("]");
175173
return sb.ToString();
176174
}
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Globalization;
4+
using System.Text;
5+
6+
namespace ProjNet.CoordinateSystems
7+
{
8+
/// <summary>
9+
/// A 1D coordinate system suitable vertical coordinates
10+
/// </summary>
11+
public class VerticalCoordinateSystem : CoordinateSystem
12+
{
13+
/// <summary>
14+
/// Creates an instance of a VerticalCoordinateSystem
15+
/// </summary>
16+
/// <param name="linearUnit">The linear unit</param>
17+
/// <param name="verticalDatum">The vertical datum</param>
18+
/// <param name="axisInfo">Axis information</param>
19+
/// <param name="name">Name</param>
20+
/// <param name="authority">Authority name</param>
21+
/// <param name="authorityCode">Authority-specific identification code.</param>
22+
/// <param name="alias">Alias</param>
23+
/// <param name="abbreviation">Abbreviation</param>
24+
/// <param name="remarks">Provider-supplied remarks</param>
25+
public VerticalCoordinateSystem(LinearUnit linearUnit, VerticalDatum verticalDatum, AxisInfo axisInfo, string name, string authority, long authorityCode, string alias, string abbreviation, string remarks) : base(name, authority, authorityCode, alias, abbreviation, remarks)
26+
{
27+
VerticalDatum = verticalDatum;
28+
AxisInfo = new List<AxisInfo>() { axisInfo };
29+
LinearUnit = linearUnit;
30+
}
31+
32+
/// <summary>
33+
/// Gets or sets the VerticalDatum
34+
/// </summary>
35+
public VerticalDatum VerticalDatum { get; set; }
36+
37+
/// <summary>
38+
/// Gets or sets the LinearUnit
39+
/// </summary>
40+
public LinearUnit LinearUnit { get; set; }
41+
42+
/// <summary>
43+
/// Creates a meter unit coordinate system with <see cref="VerticalDatum.ODN"/>
44+
/// </summary>
45+
public static VerticalCoordinateSystem ODN =>
46+
new VerticalCoordinateSystem(
47+
new LinearUnit(1, "metre", "EPSG", 9001, string.Empty, "m", string.Empty)
48+
, VerticalDatum.ODN, new AxisInfo("Up", AxisOrientationEnum.Up)
49+
, "Newlyn"
50+
, "EPSG"
51+
, 5701
52+
, string.Empty
53+
, "ODN"
54+
, string.Empty
55+
);
56+
/// <inheritdoc/>
57+
public override string WKT
58+
{
59+
get
60+
{
61+
var sb = new StringBuilder();
62+
sb.AppendFormat("VERT_CS[\"{0}\", {1}, {2}", Name, VerticalDatum.WKT, LinearUnit.WKT);
63+
//Skip axis info if they contain default values
64+
if (AxisInfo.Count != 1 ||
65+
AxisInfo[0].Name != "Up" || AxisInfo[0].Orientation != AxisOrientationEnum.Up)
66+
{
67+
sb.AppendFormat(", {0}", GetAxis(0).WKT);
68+
}
69+
if (!string.IsNullOrWhiteSpace(Authority) && AuthorityCode > 0)
70+
sb.AppendFormat(", AUTHORITY[\"{0}\", \"{1}\"]", Authority, AuthorityCode);
71+
sb.Append("]");
72+
return sb.ToString();
73+
}
74+
}
75+
76+
/// <inheritdoc/>
77+
public override string XML
78+
{
79+
get
80+
{
81+
var sb = new StringBuilder();
82+
sb.AppendFormat(CultureInfo.InvariantCulture.NumberFormat,
83+
"<CS_CoordinateSystem Dimension=\"{0}\"><CS_VerticalCoordinateSystem>{1}",
84+
this.Dimension, InfoXml);
85+
foreach (var ai in AxisInfo)
86+
sb.Append(ai.XML);
87+
sb.AppendFormat("{0}{1}</CS_VerticalCoordinateSystem></CS_CoordinateSystem>",
88+
VerticalDatum.XML, LinearUnit.XML);
89+
return sb.ToString();
90+
}
91+
}
92+
93+
/// <inheritdoc/>
94+
public override bool EqualParams(object obj)
95+
{
96+
if (!(obj is VerticalCoordinateSystem vcs))
97+
return false;
98+
99+
if (vcs.Dimension != Dimension) return false;
100+
if (AxisInfo.Count != vcs.AxisInfo.Count) return false;
101+
for (int i = 0; i < vcs.AxisInfo.Count; i++)
102+
if (vcs.AxisInfo[i].Orientation != AxisInfo[i].Orientation)
103+
return false;
104+
return vcs.LinearUnit.EqualParams(LinearUnit) &&
105+
vcs.VerticalDatum.EqualParams(VerticalDatum);
106+
}
107+
108+
/// <inheritdoc/>
109+
public override IUnit GetUnits(int dimension)
110+
{
111+
if( dimension != 0 )
112+
{
113+
throw new ArgumentOutOfRangeException(nameof(dimension), "Vertical Coordinate Systems have only one dimension");
114+
}
115+
116+
return LinearUnit;
117+
}
118+
}
119+
}

0 commit comments

Comments
 (0)