diff --git a/MapControl/Shared/Etrs89UtmProjection.cs b/MapControl/Shared/Etrs89UtmProjection.cs new file mode 100644 index 00000000..718aaa82 --- /dev/null +++ b/MapControl/Shared/Etrs89UtmProjection.cs @@ -0,0 +1,40 @@ +// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control +// © 2022 Clemens Fischer +// Licensed under the Microsoft Public License (Ms-PL) + +using System; + +namespace MapControl +{ + /// + /// ETRS89 UTM Projection with zone number. + /// + public class Etrs89UtmProjection : TransverseMercatorProjection + { + public const int FirstZone = 28; + public const int LastZone = 38; + public const int FirstZoneEpsgCode = 25800 + FirstZone; + public const int LastZoneEpsgCode = 25800 + LastZone; + + public int Zone { get; } + + public Etrs89UtmProjection(int zone) + { + if (zone < FirstZone || zone > LastZone) + { + throw new ArgumentException($"Invalid ETRS89 UTM zone {zone}.", nameof(zone)); + } + + Zone = zone; + CrsId = $"EPSG:{Zone - FirstZone + FirstZoneEpsgCode}"; + + // GRS 1980 + EquatorialRadius = 6378137d; + Flattening = 1d / 298.257222101; + ScaleFactor = DefaultScaleFactor; + CentralMeridian = Zone * 6d - 183d; + FalseEasting = 5e5; + FalseNorthing = 0d; + } + } +} diff --git a/MapControl/Shared/MapBase.cs b/MapControl/Shared/MapBase.cs index 5e53baaa..b41509d2 100644 --- a/MapControl/Shared/MapBase.cs +++ b/MapControl/Shared/MapBase.cs @@ -381,15 +381,19 @@ namespace MapControl public void ZoomToBounds(BoundingBox boundingBox) { var mapRect = MapProjection.BoundingBoxToMapRect(boundingBox); - var targetCenter = MapProjection.MapToLocation(mapRect.Center); - if (targetCenter != null) + if (mapRect != null) { - var scale = Math.Min(RenderSize.Width / mapRect.Width, RenderSize.Height / mapRect.Height); + var targetCenter = MapProjection.MapToLocation(mapRect.Center); - TargetZoomLevel = ViewTransform.ScaleToZoomLevel(scale); - TargetCenter = targetCenter; - TargetHeading = 0d; + if (targetCenter != null) + { + var scale = Math.Min(RenderSize.Width / mapRect.Width, RenderSize.Height / mapRect.Height); + + TargetZoomLevel = ViewTransform.ScaleToZoomLevel(scale); + TargetCenter = targetCenter; + TargetHeading = 0d; + } } } diff --git a/MapControl/Shared/MapProjection.cs b/MapControl/Shared/MapProjection.cs index fb5330c8..95b77c73 100644 --- a/MapControl/Shared/MapProjection.cs +++ b/MapControl/Shared/MapProjection.cs @@ -134,15 +134,11 @@ namespace MapControl // const double p = 0.01; - var bbox = string.Format(CultureInfo.InvariantCulture, "{0:F2},{1:F2},{2:F2},{3:F2}", + return string.Format(CultureInfo.InvariantCulture, "{0:F2},{1:F2},{2:F2},{3:F2}", p * Math.Ceiling(mapRect.XMin / p), p * Math.Ceiling(mapRect.YMin / p), p * Math.Floor(mapRect.XMax / p), p * Math.Floor(mapRect.YMax / p)); - - System.Diagnostics.Debug.WriteLine(bbox); - - return bbox; } } } diff --git a/MapControl/Shared/MapProjectionFactory.cs b/MapControl/Shared/MapProjectionFactory.cs index 536d2119..950a002d 100644 --- a/MapControl/Shared/MapProjectionFactory.cs +++ b/MapControl/Shared/MapProjectionFactory.cs @@ -40,6 +40,10 @@ namespace MapControl projection = new Nad27UtmProjection(epsgCode % 100); break; + case var c when c >= Nad83UtmProjection.FirstZoneEpsgCode && c <= Nad83UtmProjection.LastZoneEpsgCode: + projection = new Nad83UtmProjection(epsgCode % 100); + break; + case var c when c >= Wgs84UtmProjection.FirstZoneNorthEpsgCode && c <= Wgs84UtmProjection.LastZoneNorthEpsgCode: projection = new Wgs84UtmProjection(epsgCode % 100, true); break; diff --git a/MapControl/Shared/Nad27UtmProjection.cs b/MapControl/Shared/Nad27UtmProjection.cs new file mode 100644 index 00000000..ab0fe7bf --- /dev/null +++ b/MapControl/Shared/Nad27UtmProjection.cs @@ -0,0 +1,40 @@ +// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control +// © 2022 Clemens Fischer +// Licensed under the Microsoft Public License (Ms-PL) + +using System; + +namespace MapControl +{ + /// + /// NAD27 UTM Projection with zone number. + /// + public class Nad27UtmProjection : TransverseMercatorProjection + { + public const int FirstZone = 1; + public const int LastZone = 22; + public const int FirstZoneEpsgCode = 26700 + FirstZone; + public const int LastZoneEpsgCode = 26700 + LastZone; + + public int Zone { get; } + + public Nad27UtmProjection(int zone) + { + if (zone < FirstZone || zone > LastZone) + { + throw new ArgumentException($"Invalid NAD27 UTM zone {zone}.", nameof(zone)); + } + + Zone = zone; + CrsId = $"EPSG:{Zone - FirstZone + FirstZoneEpsgCode}"; + + // Clarke 1866 + EquatorialRadius = 6378206.4; + Flattening = 1d / 294.978698213898; + ScaleFactor = DefaultScaleFactor; + CentralMeridian = Zone * 6d - 183d; + FalseEasting = 5e5; + FalseNorthing = 0d; + } + } +} diff --git a/MapControl/Shared/Nad83UtmProjection.cs b/MapControl/Shared/Nad83UtmProjection.cs new file mode 100644 index 00000000..61aebd5d --- /dev/null +++ b/MapControl/Shared/Nad83UtmProjection.cs @@ -0,0 +1,40 @@ +// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control +// © 2022 Clemens Fischer +// Licensed under the Microsoft Public License (Ms-PL) + +using System; + +namespace MapControl +{ + /// + /// NAD83 UTM Projection with zone number. + /// + public class Nad83UtmProjection : TransverseMercatorProjection + { + public const int FirstZone = 1; + public const int LastZone = 23; + public const int FirstZoneEpsgCode = 26900 + FirstZone; + public const int LastZoneEpsgCode = 26900 + LastZone; + + public int Zone { get; } + + public Nad83UtmProjection(int zone) + { + if (zone < FirstZone || zone > LastZone) + { + throw new ArgumentException($"Invalid NAD83 UTM zone {zone}.", nameof(zone)); + } + + Zone = zone; + CrsId = $"EPSG:{Zone - FirstZone + FirstZoneEpsgCode}"; + + // GRS 1980 + EquatorialRadius = 6378137d; + Flattening = 1d / 298.257222101; + ScaleFactor = DefaultScaleFactor; + CentralMeridian = Zone * 6d - 183d; + FalseEasting = 5e5; + FalseNorthing = 0d; + } + } +} diff --git a/MapControl/Shared/UtmProjection.cs b/MapControl/Shared/Wgs84UtmProjection.cs similarity index 58% rename from MapControl/Shared/UtmProjection.cs rename to MapControl/Shared/Wgs84UtmProjection.cs index b34ce91d..29469289 100644 --- a/MapControl/Shared/UtmProjection.cs +++ b/MapControl/Shared/Wgs84UtmProjection.cs @@ -6,70 +6,6 @@ using System; namespace MapControl { - /// - /// ETRS89 UTM Projection with zone number. - /// - public class Etrs89UtmProjection : TransverseMercatorProjection - { - public const int FirstZone = 28; - public const int LastZone = 38; - public const int FirstZoneEpsgCode = 25800 + FirstZone; - public const int LastZoneEpsgCode = 25800 + LastZone; - - public int Zone { get; } - - public Etrs89UtmProjection(int zone) - { - if (zone < FirstZone || zone > LastZone) - { - throw new ArgumentException($"Invalid ETRS89 UTM zone {zone}.", nameof(zone)); - } - - Zone = zone; - CrsId = $"EPSG:{Zone - FirstZone + FirstZoneEpsgCode}"; - - // GRS 1980 - EquatorialRadius = 6378137d; - Flattening = 1d / 298.257222101; - ScaleFactor = DefaultScaleFactor; - CentralMeridian = Zone * 6d - 183d; - FalseEasting = 5e5; - FalseNorthing = 0d; - } - } - - /// - /// NAD27 UTM Projection with zone number. - /// - public class Nad27UtmProjection : TransverseMercatorProjection - { - public const int FirstZone = 1; - public const int LastZone = 22; - public const int FirstZoneEpsgCode = 26700 + FirstZone; - public const int LastZoneEpsgCode = 26700 + LastZone; - - public int Zone { get; } - - public Nad27UtmProjection(int zone) - { - if (zone < FirstZone || zone > LastZone) - { - throw new ArgumentException($"Invalid NAD27 UTM zone {zone}.", nameof(zone)); - } - - Zone = zone; - CrsId = $"EPSG:{Zone - FirstZone + FirstZoneEpsgCode}"; - - // Clarke 1866 - EquatorialRadius = 6378206.4; - Flattening = 1d / 294.978698213898; - ScaleFactor = DefaultScaleFactor; - CentralMeridian = Zone * 6d - 183d; - FalseEasting = 5e5; - FalseNorthing = 0d; - } - } - /// /// WGS84 UTM Projection with zone number and north/south flag. /// @@ -107,7 +43,7 @@ namespace MapControl IsNorth = north; CrsId = crsId ?? $"EPSG:{epsgCode}"; EquatorialRadius = Wgs84EquatorialRadius; - Flattening = 1d / Wgs84Flattening; + Flattening = Wgs84Flattening; ScaleFactor = DefaultScaleFactor; CentralMeridian = Zone * 6d - 183d; FalseEasting = 5e5; diff --git a/MapControl/Shared/WmsImageLayer.cs b/MapControl/Shared/WmsImageLayer.cs index a9ac5c95..ee199463 100644 --- a/MapControl/Shared/WmsImageLayer.cs +++ b/MapControl/Shared/WmsImageLayer.cs @@ -240,6 +240,12 @@ namespace MapControl protected virtual string GetMapRequestUri(BoundingBox boundingBox) { var mapRect = ParentMap.MapProjection.BoundingBoxToMapRect(boundingBox); + + if (mapRect == null) + { + return null; + } + var viewScale = ParentMap.ViewTransform.Scale; return GetRequestUri(new Dictionary @@ -265,6 +271,12 @@ namespace MapControl var viewSize = ParentMap.RenderSize; var boundingBox = ParentMap.ViewRectToBoundingBox(new Rect(0d, 0d, viewSize.Width, viewSize.Height)); var mapRect = ParentMap.MapProjection.BoundingBoxToMapRect(boundingBox); + + if (mapRect == null) + { + return null; + } + var viewRect = GetViewRect(mapRect); var transform = new Matrix(1, 0, 0, 1, -viewSize.Width / 2, -viewSize.Height / 2); diff --git a/MapControl/UWP/MapControl.UWP.csproj b/MapControl/UWP/MapControl.UWP.csproj index 61629a28..4ae146a5 100644 --- a/MapControl/UWP/MapControl.UWP.csproj +++ b/MapControl/UWP/MapControl.UWP.csproj @@ -71,6 +71,9 @@ EquirectangularProjection.cs + + Etrs89UtmProjection.cs + FilePath.cs @@ -143,6 +146,12 @@ MapTileLayerBase.cs + + Nad27UtmProjection.cs + + + Nad83UtmProjection.cs + OrthographicProjection.cs @@ -176,9 +185,6 @@ TransverseMercatorProjection.cs - - UtmProjection.cs - ViewportChangedEventArgs.cs @@ -191,6 +197,9 @@ WebMercatorProjection.cs + + Wgs84UtmProjection.cs + WmsImageLayer.cs diff --git a/MapControl/WPF/MapItemsImageLayer.WPF.cs b/MapControl/WPF/MapItemsImageLayer.WPF.cs index f0393372..1773b8eb 100644 --- a/MapControl/WPF/MapItemsImageLayer.WPF.cs +++ b/MapControl/WPF/MapItemsImageLayer.WPF.cs @@ -37,17 +37,21 @@ namespace MapControl if (projection != null && items != null) { - image = await Task.Run(() => GetImage(projection, boundingBox, items)); + var mapRect = projection.BoundingBoxToMapRect(boundingBox); + + if (mapRect != null) + { + image = await Task.Run(() => GetImage(projection, mapRect, items)); + } } return image; } - private DrawingImage GetImage(MapProjection projection, BoundingBox boundingBox, IEnumerable items) + private DrawingImage GetImage(MapProjection projection, MapRect mapRect, IEnumerable items) { var scale = ParentMap.ViewTransform.Scale; var rotation = ParentMap.ViewTransform.Rotation; - var mapRect = projection.BoundingBoxToMapRect(boundingBox); var drawings = new DrawingGroup(); foreach (var item in items) diff --git a/MapProjections/Shared/Ed50UtmProjection.cs b/MapProjections/Shared/Ed50UtmProjection.cs index 3550656b..fbea40ae 100644 --- a/MapProjections/Shared/Ed50UtmProjection.cs +++ b/MapProjections/Shared/Ed50UtmProjection.cs @@ -6,15 +6,26 @@ using System; namespace MapControl.Projections { + /// + /// ED50 UTM Projection with zone number. + /// public class Ed50UtmProjection : GeoApiProjection { + public const int FirstZone = 28; + public const int LastZone = 38; + public const int FirstZoneEpsgCode = 23000 + FirstZone; + public const int LastZoneEpsgCode = 23000 + LastZone; + + public int Zone { get; } + public Ed50UtmProjection(int zone) { - if (zone < 28 || zone > 38) + if (zone < FirstZone || zone > LastZone) { - throw new ArgumentException($"Invalid UTM zone {zone}.", nameof(zone)); + throw new ArgumentException($"Invalid ED50 UTM zone {zone}.", nameof(zone)); } + Zone = zone; CoordinateSystemWkt = $"PROJCS[\"ED50 / UTM zone {zone}N\"," + "GEOGCS[\"ED50\"," @@ -23,10 +34,8 @@ namespace MapControl.Projections + "AUTHORITY[\"EPSG\",\"7022\"]]," + "TOWGS84[-87,-98,-121,0,0,0,0]," + "AUTHORITY[\"EPSG\",\"6230\"]]," - + "PRIMEM[\"Greenwich\",0," - + "AUTHORITY[\"EPSG\",\"8901\"]]," - + "UNIT[\"degree\",0.0174532925199433," - + "AUTHORITY[\"EPSG\",\"9122\"]]," + + "PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]]," + + "UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]]," + "AUTHORITY[\"EPSG\",\"4230\"]]," + "PROJECTION[\"Transverse_Mercator\"]," + "PARAMETER[\"latitude_of_origin\",0]," @@ -34,8 +43,7 @@ namespace MapControl.Projections + "PARAMETER[\"scale_factor\",0.9996]," + "PARAMETER[\"false_easting\",500000]," + "PARAMETER[\"false_northing\",0]," - + "UNIT[\"metre\",1," - + "AUTHORITY[\"EPSG\",\"9001\"]]," + + "UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]]," + "AXIS[\"Easting\",EAST]," + "AXIS[\"Northing\",NORTH]," + $"AUTHORITY[\"EPSG\",\"230{zone}\"]]"; diff --git a/MapProjections/Shared/Etrs89UtmProjection.cs b/MapProjections/Shared/Etrs89UtmProjection.cs index ce707efb..fc62aa1e 100644 --- a/MapProjections/Shared/Etrs89UtmProjection.cs +++ b/MapProjections/Shared/Etrs89UtmProjection.cs @@ -6,15 +6,26 @@ using System; namespace MapControl.Projections { + /// + /// ETRS89 UTM Projection with zone number. + /// public class Etrs89UtmProjection : GeoApiProjection { + public const int FirstZone = 28; + public const int LastZone = 38; + public const int FirstZoneEpsgCode = 25800 + FirstZone; + public const int LastZoneEpsgCode = 25800 + LastZone; + + public int Zone { get; } + public Etrs89UtmProjection(int zone) { - if (zone < 28 || zone > 38) + if (zone < FirstZone || zone > LastZone) { - throw new ArgumentException($"Invalid UTM zone {zone}.", nameof(zone)); + throw new ArgumentException($"Invalid ETRS89 UTM zone {zone}.", nameof(zone)); } + Zone = zone; CoordinateSystemWkt = $"PROJCS[\"ETRS89 / UTM zone {zone}N\"," + "GEOGCS[\"ETRS89\"," @@ -23,10 +34,8 @@ namespace MapControl.Projections + "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\"]]," + + "PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]]," + + "UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]]," + "AUTHORITY[\"EPSG\",\"4258\"]]," + "PROJECTION[\"Transverse_Mercator\"]," + "PARAMETER[\"latitude_of_origin\",0]," @@ -34,8 +43,7 @@ namespace MapControl.Projections + "PARAMETER[\"scale_factor\",0.9996]," + "PARAMETER[\"false_easting\",500000]," + "PARAMETER[\"false_northing\",0]," - + "UNIT[\"metre\",1," - + "AUTHORITY[\"EPSG\",\"9001\"]]," + + "UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]]," + "AXIS[\"Easting\",EAST]," + "AXIS[\"Northing\",NORTH]," + $"AUTHORITY[\"EPSG\",\"258{zone}\"]]"; diff --git a/MapProjections/Shared/GeoApiProjectionFactory.cs b/MapProjections/Shared/GeoApiProjectionFactory.cs index b6e56647..c7da9b0a 100644 --- a/MapProjections/Shared/GeoApiProjectionFactory.cs +++ b/MapProjections/Shared/GeoApiProjectionFactory.cs @@ -8,17 +8,6 @@ namespace MapControl.Projections { public class GeoApiProjectionFactory : MapProjectionFactory { - private const int WorldMercator = 3395; - private const int WebMercator = 3857; - private const int Ed50UtmFirst = 23028; - private const int Ed50UtmLast = 23038; - private const int Etrs89UtmFirst = 25828; - private const int Etrs89UtmLast = 25838; - private const int Wgs84UtmNorthFirst = 32601; - private const int Wgs84UtmNorthLast = 32660; - private const int Wgs84UtmSouthFirst = 32701; - private const int Wgs84UtmSouthLast = 32760; - public Dictionary CoordinateSystemWkts { get; } = new Dictionary(); public override MapProjection GetProjection(int epsgCode) @@ -33,27 +22,35 @@ namespace MapControl.Projections { switch (epsgCode) { - case WorldMercator: + case WorldMercatorProjection.DefaultEpsgCode: projection = new WorldMercatorProjection(); break; - case WebMercator: + case WebMercatorProjection.DefaultEpsgCode: projection = new WebMercatorProjection(); break; - case int c when c >= Ed50UtmFirst && c <= Ed50UtmLast: + case int c when c >= Ed50UtmProjection.FirstZoneEpsgCode && c <= Ed50UtmProjection.LastZoneEpsgCode: projection = new Ed50UtmProjection(epsgCode % 100); break; - case int c when c >= Etrs89UtmFirst && c <= Etrs89UtmLast: + case var c when c >= Etrs89UtmProjection.FirstZoneEpsgCode && c <= Etrs89UtmProjection.LastZoneEpsgCode: projection = new Etrs89UtmProjection(epsgCode % 100); break; - case int c when c >= Wgs84UtmNorthFirst && c <= Wgs84UtmNorthLast: + case var c when c >= Nad27UtmProjection.FirstZoneEpsgCode && c <= Nad27UtmProjection.LastZoneEpsgCode: + projection = new Nad27UtmProjection(epsgCode % 100); + break; + + case var c when c >= Nad83UtmProjection.FirstZoneEpsgCode && c <= Nad83UtmProjection.LastZoneEpsgCode: + projection = new Nad83UtmProjection(epsgCode % 100); + break; + + case var c when c >= Wgs84UtmProjection.FirstZoneNorthEpsgCode && c <= Wgs84UtmProjection.LastZoneNorthEpsgCode: projection = new Wgs84UtmProjection(epsgCode % 100, true); break; - case int c when c >= Wgs84UtmSouthFirst && c <= Wgs84UtmSouthLast: + case var c when c >= Wgs84UtmProjection.FirstZoneSouthEpsgCode && c <= Wgs84UtmProjection.LastZoneSouthEpsgCode: projection = new Wgs84UtmProjection(epsgCode % 100, false); break; diff --git a/MapProjections/Shared/Nad27UtmProjection.cs b/MapProjections/Shared/Nad27UtmProjection.cs new file mode 100644 index 00000000..b3aae06e --- /dev/null +++ b/MapProjections/Shared/Nad27UtmProjection.cs @@ -0,0 +1,50 @@ +// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control +// © 2022 Clemens Fischer +// Licensed under the Microsoft Public License (Ms-PL) + +using System; + +namespace MapControl.Projections +{ + /// + /// NAD27 UTM Projection with zone number. + /// + public class Nad27UtmProjection : GeoApiProjection + { + public const int FirstZone = 1; + public const int LastZone = 22; + public const int FirstZoneEpsgCode = 26700 + FirstZone; + public const int LastZoneEpsgCode = 26700 + LastZone; + + public int Zone { get; } + + public Nad27UtmProjection(int zone) + { + if (zone < FirstZone || zone > LastZone) + { + throw new ArgumentException($"Invalid NAD27 UTM zone {zone}.", nameof(zone)); + } + + Zone = zone; + CoordinateSystemWkt + = $"PROJCS[\"NAD27 / UTM zone {zone}N\"," + + "GEOGCS[\"NAD27\"," + + "DATUM[\"North_American_Datum_1927\"," + + "SPHEROID[\"Clarke 1866\",6378206.4,294.978698213898]," + + "]," //"EXTENSION[\"PROJ4_GRIDS\",\"NTv2_0.gsb\"]]," -- not recognized + + "PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]]," + + "UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]]," + + "AUTHORITY[\"EPSG\",\"4267\"]]," + + "PROJECTION[\"Transverse_Mercator\"]," + + "PARAMETER[\"latitude_of_origin\",0]," + + $"PARAMETER[\"central_meridian\",{6 * zone - 183}]," + + "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\",\"267{zone}\"]]"; + } + } +} diff --git a/MapProjections/Shared/Nad83UtmProjection.cs b/MapProjections/Shared/Nad83UtmProjection.cs new file mode 100644 index 00000000..47265657 --- /dev/null +++ b/MapProjections/Shared/Nad83UtmProjection.cs @@ -0,0 +1,50 @@ +// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control +// © 2022 Clemens Fischer +// Licensed under the Microsoft Public License (Ms-PL) + +using System; + +namespace MapControl.Projections +{ + /// + /// NAD83 UTM Projection with zone number. + /// + public class Nad83UtmProjection : GeoApiProjection + { + public const int FirstZone = 1; + public const int LastZone = 23; + public const int FirstZoneEpsgCode = 26900 + FirstZone; + public const int LastZoneEpsgCode = 26900 + LastZone; + + public int Zone { get; } + + public Nad83UtmProjection(int zone) + { + if (zone < FirstZone || zone > LastZone) + { + throw new ArgumentException($"Invalid NAD83 UTM zone {zone}.", nameof(zone)); + } + + Zone = zone; + CoordinateSystemWkt + = $"PROJCS[\"NAD83 / UTM zone {zone}N\"," + + "GEOGCS[\"NAD83\"," + + "DATUM[\"North_American_Datum_1983\"," + + "SPHEROID[\"GRS 1980\",6378137,298.257222101]," + + "TOWGS84[0,0,0,0,0,0,0]]," + + "PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]]," + + "UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]]," + + "AUTHORITY[\"EPSG\",\"4269\"]]," + + "PROJECTION[\"Transverse_Mercator\"]," + + "PARAMETER[\"latitude_of_origin\",0]," + + $"PARAMETER[\"central_meridian\",{6 * zone - 183}]," + + "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\",\"269{zone}\"]]"; + } + } +} diff --git a/MapProjections/Shared/WebMercatorProjection.cs b/MapProjections/Shared/WebMercatorProjection.cs index 19b4f559..ffaed777 100644 --- a/MapProjections/Shared/WebMercatorProjection.cs +++ b/MapProjections/Shared/WebMercatorProjection.cs @@ -13,6 +13,8 @@ namespace MapControl.Projections /// public class WebMercatorProjection : GeoApiProjection { + public const int DefaultEpsgCode = 3857; + public WebMercatorProjection() { CoordinateSystem = ProjectedCoordinateSystem.WebMercator; diff --git a/MapProjections/Shared/Wgs84AutoUtmProjection.cs b/MapProjections/Shared/Wgs84AutoUtmProjection.cs deleted file mode 100644 index 3b7c00cf..00000000 --- a/MapProjections/Shared/Wgs84AutoUtmProjection.cs +++ /dev/null @@ -1,56 +0,0 @@ -// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control -// © 2022 Clemens Fischer -// Licensed under the Microsoft Public License (Ms-PL) - -using ProjNet.CoordinateSystems; -using System; - -namespace MapControl.Projections -{ - public class Wgs84AutoUtmProjection : GeoApiProjection - { - public const string DefaultCrsId = "AUTO2:42001"; - - public Wgs84AutoUtmProjection(bool useZoneCrsId = false) - { - UseZoneCrsId = useZoneCrsId; - UpdateZone(); - } - - public int Zone { get; private set; } - public bool IsNorth { get; private set; } - public bool UseZoneCrsId { get; } - - public override Location Center - { - get => base.Center; - set - { - if (!Equals(base.Center, value)) - { - base.Center = value; - UpdateZone(); - } - } - } - - private void UpdateZone() - { - var lon = Location.NormalizeLongitude(Center.Longitude); - var zone = (int)Math.Floor(lon / 6d) + 31; - var north = Center.Latitude >= 0d; - - if (Zone != zone || IsNorth != north) - { - Zone = zone; - IsNorth = north; - CoordinateSystem = ProjectedCoordinateSystem.WGS84_UTM(Zone, IsNorth); - - if (!UseZoneCrsId) - { - CrsId = DefaultCrsId; - } - } - } - } -} diff --git a/MapProjections/Shared/Wgs84UtmProjection.cs b/MapProjections/Shared/Wgs84UtmProjection.cs index 1072853e..1ea655f0 100644 --- a/MapProjections/Shared/Wgs84UtmProjection.cs +++ b/MapProjections/Shared/Wgs84UtmProjection.cs @@ -7,16 +7,86 @@ using System; namespace MapControl.Projections { + /// + /// WGS84 UTM Projection with zone number and north/south flag. + /// public class Wgs84UtmProjection : GeoApiProjection { + public const int FirstZone = 1; + public const int LastZone = 60; + public const int FirstZoneNorthEpsgCode = 32600 + FirstZone; + public const int LastZoneNorthEpsgCode = 32600 + LastZone; + public const int FirstZoneSouthEpsgCode = 32700 + FirstZone; + public const int LastZoneSouthEpsgCode = 32700 + LastZone; + + public int Zone { get; private set; } + public bool IsNorth { get; private set; } + + protected Wgs84UtmProjection() + { + } + public Wgs84UtmProjection(int zone, bool north) { - if (zone < 1 || zone > 60) + SetZone(zone, north); + } + + protected void SetZone(int zone, bool north) + { + if (zone < FirstZone || zone > LastZone) { - throw new ArgumentException($"Invalid UTM zone {zone}.", nameof(zone)); + throw new ArgumentException($"Invalid WGS84 UTM zone {zone}.", nameof(zone)); } - CoordinateSystem = ProjectedCoordinateSystem.WGS84_UTM(zone, north); + Zone = zone; + IsNorth = north; + CoordinateSystem = ProjectedCoordinateSystem.WGS84_UTM(Zone, IsNorth); + } + } + + /// + /// WGS84 UTM Projection with automatic zone selection from projection center. + /// + public class Wgs84AutoUtmProjection : Wgs84UtmProjection + { + public const string DefaultCrsId = "AUTO2:42001"; + + public Wgs84AutoUtmProjection(bool useZoneCrsId = false) + { + UseZoneCrsId = useZoneCrsId; + UpdateZone(); + } + + public bool UseZoneCrsId { get; } + + public override Location Center + { + get => base.Center; + set + { + if (!Equals(base.Center, value)) + { + base.Center = value; + UpdateZone(); + } + } + } + + private void UpdateZone() + { + var lon = Location.NormalizeLongitude(Center.Longitude); + var zone = (int)Math.Floor(lon / 6d) + 31; + var north = Center.Latitude >= 0d; + + if (Zone != zone || IsNorth != north || string.IsNullOrEmpty(CrsId)) + { + SetZone(zone, north); + + if (!UseZoneCrsId) + { + CrsId = DefaultCrsId; + } + } } } } diff --git a/MapProjections/Shared/WorldMercatorProjection.cs b/MapProjections/Shared/WorldMercatorProjection.cs index 185f081e..b7a73060 100644 --- a/MapProjections/Shared/WorldMercatorProjection.cs +++ b/MapProjections/Shared/WorldMercatorProjection.cs @@ -12,6 +12,8 @@ namespace MapControl.Projections /// public class WorldMercatorProjection : GeoApiProjection { + public const int DefaultEpsgCode = 3395; + public WorldMercatorProjection() { CoordinateSystemWkt diff --git a/MapProjections/UWP/MapProjections.UWP.csproj b/MapProjections/UWP/MapProjections.UWP.csproj index 4ae24b5b..6ce0adee 100644 --- a/MapProjections/UWP/MapProjections.UWP.csproj +++ b/MapProjections/UWP/MapProjections.UWP.csproj @@ -55,9 +55,6 @@ WebMercatorProjection.cs - - Wgs84AutoUtmProjection.cs - Wgs84UtmProjection.cs diff --git a/SampleApps/Shared/UTM2GTIF.tiff b/SampleApps/Shared/UTM2GTIF.tiff new file mode 100644 index 00000000..a0fde79e Binary files /dev/null and b/SampleApps/Shared/UTM2GTIF.tiff differ diff --git a/SampleApps/UniversalApp/MainPage.xaml b/SampleApps/UniversalApp/MainPage.xaml index 96c756d9..d4b58aab 100644 --- a/SampleApps/UniversalApp/MainPage.xaml +++ b/SampleApps/UniversalApp/MainPage.xaml @@ -210,6 +210,19 @@ ServiceUri="http://ows.terrestris.de/osm/service"/> + + + + + + + + + + + + + @@ -220,18 +233,8 @@ - - - - - - - - - - - - + + @@ -242,6 +245,8 @@ Map="{Binding ElementName=map}"> + + 10_535_330.jpg + + UTM2GTIF.tiff + diff --git a/SampleApps/WinUiApp/MainWindow.xaml b/SampleApps/WinUiApp/MainWindow.xaml index 6e23ad9d..2971ba62 100644 --- a/SampleApps/WinUiApp/MainWindow.xaml +++ b/SampleApps/WinUiApp/MainWindow.xaml @@ -218,6 +218,19 @@ ServiceUri="http://ows.terrestris.de/osm/service"/> + + + + + + + + + + + + + @@ -228,18 +241,8 @@ - - - - - - - - - - - - + + @@ -249,6 +252,8 @@ Map="{Binding ElementName=map}"> + + + diff --git a/SampleApps/WpfApplication/MainWindow.xaml b/SampleApps/WpfApplication/MainWindow.xaml index dfbfe390..e04bfa68 100644 --- a/SampleApps/WpfApplication/MainWindow.xaml +++ b/SampleApps/WpfApplication/MainWindow.xaml @@ -184,6 +184,13 @@ + + + + + + + @@ -191,12 +198,8 @@ - - - - - - + + @@ -206,6 +209,8 @@ Map="{Binding ElementName=map}"> + + Always + + Always + + + + + Never + + \ No newline at end of file