diff --git a/MapProjections/Shared/GeoApiProjection.cs b/MapProjections/Shared/GeoApiProjection.cs index 62f55048..d59ae7b0 100644 --- a/MapProjections/Shared/GeoApiProjection.cs +++ b/MapProjections/Shared/GeoApiProjection.cs @@ -30,8 +30,24 @@ namespace MapControl.Projections private double scaleFactor; private string bboxFormat; - public IMathTransform LocationToMapTransform { get; private set; } - public IMathTransform MapToLocationTransform { get; private set; } + public GeoApiProjection(string wkt = null) + { + if (wkt != null) + { + WKT = wkt; + } + } + + /// + /// Gets or sets an OGC Well-known text representation of a coordinate system, + /// i.e. a PROJCS[...] or GEOGCS[...] string as used by https://epsg.io or http://spatialreference.org. + /// Setting this property updates the CoordinateSystem property with an ICoordinateSystem created from the WKT string. + /// + public string WKT + { + get { return CoordinateSystem?.WKT; } + set { CoordinateSystem = new CoordinateSystemFactory().CreateFromWkt(value); } + } /// /// Gets or sets the ICoordinateSystem of the MapProjection. @@ -85,16 +101,9 @@ namespace MapControl.Projections } } - /// - /// Gets or sets an OGC Well-known text representation of a coordinate system, - /// i.e. a PROJCS[...] or GEOGCS[...] string as used by https://epsg.io or http://spatialreference.org. - /// Setting this property updates the CoordinateSystem property with an ICoordinateSystem created from the WKT string. - /// - public string WKT - { - get { return CoordinateSystem?.WKT; } - set { CoordinateSystem = new CoordinateSystemFactory().CreateFromWkt(value); } - } + public IMathTransform LocationToMapTransform { get; private set; } + + public IMathTransform MapToLocationTransform { get; private set; } public override bool IsNormalCylindrical { @@ -106,15 +115,6 @@ namespace MapControl.Projections get { return isWebMercator; } } - - public GeoApiProjection(string wkt = null) - { - if (wkt != null) - { - WKT = wkt; - } - } - public override Point LocationToMap(Location location) { if (LocationToMapTransform == null) diff --git a/MapProjections/Shared/GeoApiProjectionFactory.cs b/MapProjections/Shared/GeoApiProjectionFactory.cs index e2d771a5..c29be7ca 100644 --- a/MapProjections/Shared/GeoApiProjectionFactory.cs +++ b/MapProjections/Shared/GeoApiProjectionFactory.cs @@ -11,40 +11,90 @@ namespace MapControl.Projections { public class GeoApiProjectionFactory : MapProjectionFactory { + public const int WorldMercator = 3395; + public const int WebMercator = 3857; + public const int Etrs89UtmNorthFirst = 25828; + public const int Etrs89UtmNorthLast = 25838; + public const int Wgs84UtmNorthFirst = 32601; + public const int Wgs84UtmNorthLast = 32660; + public const int Wgs84UpsNorth = 32661; + public const int Wgs84UtmSouthFirst = 32701; + public const int Wgs84UtmSouthLast = 32760; + public const int Wgs84UpsSouth = 32761; + private readonly Dictionary wkts = new Dictionary(); private readonly HttpClient httpClient = new HttpClient(); - public override MapProjection CreateProjection(string projectionDefinition) + public override MapProjection CreateProjection(string crsId) { - var projection = base.CreateProjection(projectionDefinition); + MapProjection projection = null; - if (projection == null && - projectionDefinition.StartsWith("EPSG:") && - int.TryParse(projectionDefinition.Substring(5), out int epsgCode)) + if (crsId.StartsWith("EPSG:") && int.TryParse(crsId.Substring(5), out int epsgCode)) { - if (epsgCode >= 32601 && epsgCode <= 32660) + switch (epsgCode) { - projection = new UtmProjection(epsgCode - 32600, true); - } - else if (epsgCode == 32661) - { - projection = new UpsNorthProjection(); - } - else if (epsgCode >= 32701 && epsgCode <= 32760) - { - projection = new UtmProjection(epsgCode - 32700, false); - } - else if (epsgCode == 32761) - { - projection = new UpsSouthProjection(); - } - else - { - projection = new GeoApiProjection(GetWkt(epsgCode)); + case WorldMercator: + projection = new WorldMercatorProjection(); + break; + + case WebMercator: + projection = new WebMercatorProjection(); + break; + + case int c when c >= Etrs89UtmNorthFirst && c <= Etrs89UtmNorthLast: + projection = new GeoApiProjection(GetEtrs89UtmWkt(epsgCode)); + break; + + case int c when c >= Wgs84UtmNorthFirst && c <= Wgs84UtmNorthLast: + projection = new UtmProjection(epsgCode - Wgs84UtmNorthFirst + 1, true); + break; + + case int c when c >= Wgs84UtmSouthFirst && c <= Wgs84UtmSouthLast: + projection = new UtmProjection(epsgCode - Wgs84UtmSouthFirst + 1, false); + break; + + case Wgs84UpsNorth: + projection = new UpsNorthProjection(); + break; + + case Wgs84UpsSouth: + projection = new UpsSouthProjection(); + break; + + default: + projection = new GeoApiProjection(GetWkt(epsgCode)); + break; } } - return projection; + return projection ?? base.CreateProjection(crsId); + } + + private static string GetEtrs89UtmWkt(int epsgCode) + { + const string etrs89UtmWktFormat + = "PROJCS[\"ETRS89 / UTM zone {1}N\"," + + "GEOGCS[\"ETRS89\"," + + "DATUM[\"European_Terrestrial_Reference_System_1989\"," + + "SPHEROID[\"GRS 1980\",6378137,298.257222101,AUTHORITY[\"EPSG\",\"7019\"]]," + + "TOWGS84[0,0,0,0,0,0,0],AUTHORITY[\"EPSG\",\"6258\"]]," + + "PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]]," + + "UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]]," + + "AUTHORITY[\"EPSG\",\"4258\"]]," + + "PROJECTION[\"Transverse_Mercator\"]," + + "PARAMETER[\"latitude_of_origin\",0]," + + "PARAMETER[\"central_meridian\",{2}]," + + "PARAMETER[\"scale_factor\",0.9996]," + + "PARAMETER[\"false_easting\",500000]," + + "PARAMETER[\"false_northing\",0]," + + "UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]]," + + "AXIS[\"Easting\",EAST]," + + "AXIS[\"Northing\",NORTH]," + + "AUTHORITY[\"EPSG\",\"{0}\"]]"; + + int centralMeridian = 6 * (epsgCode - Etrs89UtmNorthFirst) - 15; + + return string.Format(etrs89UtmWktFormat, epsgCode, epsgCode - 25800, centralMeridian); } private string GetWkt(int epsgCode) diff --git a/MapProjections/Shared/PolarStereographicProjection.cs b/MapProjections/Shared/PolarStereographicProjection.cs index 374c2019..71a6fe48 100644 --- a/MapProjections/Shared/PolarStereographicProjection.cs +++ b/MapProjections/Shared/PolarStereographicProjection.cs @@ -16,15 +16,15 @@ namespace MapControl.Projections /// public class PolarStereographicProjection : MapProjection { - public static double ConvergenceTolerance = 1e-6; - public static int MaxIterations = 10; + public static double ConvergenceTolerance { get; set; } = 1e-6; + public static int MaxIterations { get; set; } = 10; private readonly bool north; private readonly double scaleFactor; private readonly double falseEasting; private readonly double falseNorthing; - public PolarStereographicProjection(string crsId, bool north, double scaleFactor = 1d, double falseEasting = 0d, double falseNorthing = 0d) + public PolarStereographicProjection(string crsId, bool north, double scaleFactor, double falseEasting, double falseNorthing) { CrsId = crsId; this.north = north; diff --git a/MapProjections/Shared/UtmProjection.cs b/MapProjections/Shared/UtmProjection.cs index b584b95f..c5c9dcf9 100644 --- a/MapProjections/Shared/UtmProjection.cs +++ b/MapProjections/Shared/UtmProjection.cs @@ -2,47 +2,20 @@ // © 2022 Clemens Fischer // Licensed under the Microsoft Public License (Ms-PL) -using System; using ProjNet.CoordinateSystems; +using System; namespace MapControl.Projections { public class UtmProjection : GeoApiProjection { - private string zone; - - public string Zone - { - get { return zone; } - set - { - if (zone != value) - { - if (string.IsNullOrEmpty(value)) - { - throw new ArgumentException("Invalid UTM zone."); - } - - var hemisphere = value[value.Length - 1]; - - if ((hemisphere != 'N' && hemisphere != 'S') || - !int.TryParse(value.Substring(0, value.Length - 1), out int zoneNumber)) - { - throw new ArgumentException("Invalid UTM zone."); - } - - SetZone(zoneNumber, hemisphere == 'N'); - } - } - } - public UtmProjection() { } - public UtmProjection(int zoneNumber, bool north) + public UtmProjection(int zone, bool north) { - SetZone(zoneNumber, north); + SetZone(zone, north); } public UtmProjection(Location location) @@ -50,27 +23,21 @@ namespace MapControl.Projections SetZone(location); } - public void SetZone(int zoneNumber, bool north) - { - if (zoneNumber < 1 || zoneNumber > 60) - { - throw new ArgumentException("Invalid UTM zone number.", nameof(zoneNumber)); - } - - var zoneName = zoneNumber.ToString() + (north ? "N" : "S"); - - if (zone != zoneName) - { - zone = zoneName; - CoordinateSystem = ProjectedCoordinateSystem.WGS84_UTM(zoneNumber, north); - } - } - public void SetZone(Location location) { var zoneNumber = Math.Min((int)(Location.NormalizeLongitude(location.Longitude) + 180d) / 6 + 1, 60); SetZone(zoneNumber, location.Latitude >= 0d); } + + public void SetZone(int zone, bool north) + { + if (zone < 1 || zone > 60) + { + throw new ArgumentException("Invalid UTM zone number.", nameof(zone)); + } + + CoordinateSystem = ProjectedCoordinateSystem.WGS84_UTM(zone, north); + } } } diff --git a/MapProjections/Shared/WorldMercatorProjection.cs b/MapProjections/Shared/WorldMercatorProjection.cs index cd202736..9ff29bf4 100644 --- a/MapProjections/Shared/WorldMercatorProjection.cs +++ b/MapProjections/Shared/WorldMercatorProjection.cs @@ -17,24 +17,24 @@ namespace MapControl.Projections { public WorldMercatorProjection() { - WKT = "PROJCS[\"WGS 84 / World 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[\"latitude_of_origin\",0]," + - "PARAMETER[\"central_meridian\",0]," + - "PARAMETER[\"scale_factor\",1]," + - "PARAMETER[\"false_easting\",0]," + - "PARAMETER[\"false_northing\",0]," + - "UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]]," + - "AXIS[\"Easting\",EAST]," + - "AXIS[\"Northing\",NORTH]," + - "AUTHORITY[\"EPSG\",\"3395\"]]"; + WKT = "PROJCS[\"WGS 84 / World 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[\"latitude_of_origin\",0]," + + "PARAMETER[\"central_meridian\",0]," + + "PARAMETER[\"scale_factor\",1]," + + "PARAMETER[\"false_easting\",0]," + + "PARAMETER[\"false_northing\",0]," + + "UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]]," + + "AXIS[\"Easting\",EAST]," + + "AXIS[\"Northing\",NORTH]," + + "AUTHORITY[\"EPSG\",\"3395\"]]"; } public override Vector GetRelativeScale(Location location)