diff --git a/MapControl/Avalonia/MapPanel.Avalonia.cs b/MapControl/Avalonia/MapPanel.Avalonia.cs index 7686c819..a4778b13 100644 --- a/MapControl/Avalonia/MapPanel.Avalonia.cs +++ b/MapControl/Avalonia/MapPanel.Avalonia.cs @@ -13,8 +13,8 @@ namespace MapControl public static readonly AttachedProperty BoundingBoxProperty = DependencyPropertyHelper.RegisterAttached("BoundingBox", typeof(MapPanel)); - public static readonly AttachedProperty MapRectProperty = - DependencyPropertyHelper.RegisterAttached("MapRect", typeof(MapPanel)); + public static readonly AttachedProperty MapRectProperty = + DependencyPropertyHelper.RegisterAttached("MapRect", typeof(MapPanel)); static MapPanel() { diff --git a/MapControl/Avalonia/MapPolypoint.Avalonia.cs b/MapControl/Avalonia/MapPolypoint.Avalonia.cs index f3111d30..6f25d7ec 100644 --- a/MapControl/Avalonia/MapPolypoint.Avalonia.cs +++ b/MapControl/Avalonia/MapPolypoint.Avalonia.cs @@ -41,10 +41,7 @@ namespace MapControl private void AddPolylinePoints(PathFigures figures, IEnumerable locations, double longitudeOffset, bool closed) { - var points = locations - .Select(location => LocationToView(location, longitudeOffset)) - .Where(point => point.HasValue) - .Select(point => point.Value); + var points = locations.Select(location => LocationToView(location, longitudeOffset)); if (points.Any()) { diff --git a/MapControl/Shared/AzimuthalEquidistantProjection.cs b/MapControl/Shared/AzimuthalEquidistantProjection.cs deleted file mode 100644 index 977382ca..00000000 --- a/MapControl/Shared/AzimuthalEquidistantProjection.cs +++ /dev/null @@ -1,78 +0,0 @@ -using System; -#if WPF -using System.Windows; -using System.Windows.Media; -#elif AVALONIA -using Avalonia; -#endif - -namespace MapControl -{ - /// - /// Spherical Azimuthal Equidistant Projection - AUTO2:97003. - /// See "Map Projections - A Working Manual" (https://pubs.usgs.gov/publication/pp1395), p.195-197. - /// - public class AzimuthalEquidistantProjection : AzimuthalProjection - { - public const string DefaultCrsId = "AUTO2:97003"; // GeoServer non-standard CRS identifier - - public AzimuthalEquidistantProjection() // parameterless constructor for XAML - : this(DefaultCrsId) - { - } - - public AzimuthalEquidistantProjection(string crsId) - { - CrsId = crsId; - } - - public override Matrix RelativeTransform(double latitude, double longitude) - { - var p = GetProjectedPoint(latitude, longitude); - var scaleX = 1d; - var scaleY = 1d; - - if (p.CosC == -1d) - { - scaleY = double.PositiveInfinity; - } - else if (p.CosC != 1d) - { - var c = Math.Acos(p.CosC); - var k = c / Math.Sin(c); // p.195 (25-2) - - (scaleX, scaleY) = p.RelativeScale(1d, k); - } - - return RelativeTransform(latitude, longitude, scaleX, scaleY); - } - - public override Point? LocationToMap(double latitude, double longitude) - { - var p = GetProjectedPoint(latitude, longitude); - - if (p.CosC == 1d) // p.195 "If cos c = 1, ... k' = 1, and x = y = 0." - { - return new Point(); - } - - if (p.CosC == -1) - { - return null; // p.195 "If cos c = -1, the point ... is plotted as a circle of radius πR." - } - - var c = Math.Acos(p.CosC); - var k = c / Math.Sin(c); // p.195 (25-2) - - return new Point(EquatorialRadius * k * p.X, EquatorialRadius * k * p.Y); // p.195 (22-4/5) - } - - public override Location MapToLocation(double x, double y) - { - var rho = Math.Sqrt(x * x + y * y); - var c = rho / EquatorialRadius; // p.196 (25-15) - - return GetLocation(x, y, rho, Math.Sin(c)); - } - } -} diff --git a/MapControl/Shared/AzimuthalProjection.cs b/MapControl/Shared/AzimuthalProjection.cs deleted file mode 100644 index 9e54aa42..00000000 --- a/MapControl/Shared/AzimuthalProjection.cs +++ /dev/null @@ -1,101 +0,0 @@ -using System; - -namespace MapControl -{ - public abstract class AzimuthalProjection : MapProjection - { - protected AzimuthalProjection() - { - EnableCenterUpdates(); - } - - public readonly struct ProjectedPoint - { - public double X { get; } - public double Y { get; } - public double CosC { get; } - - public ProjectedPoint(double centerLatitude, double centerLongitude, double latitude, double longitude) - { - var phi = latitude * Math.PI / 180d; - var phi1 = centerLatitude * Math.PI / 180d; - var dLambda = (longitude - centerLongitude) * Math.PI / 180d; // λ - λ0 - var cosPhi = Math.Cos(phi); - var sinPhi = Math.Sin(phi); - var cosPhi1 = Math.Cos(phi1); - var sinPhi1 = Math.Sin(phi1); - var cosLambda = Math.Cos(dLambda); - var sinLambda = Math.Sin(dLambda); - var cosC = sinPhi1 * sinPhi + cosPhi1 * cosPhi * cosLambda; // (5-3) - - X = cosPhi * sinLambda; - Y = cosPhi1 * sinPhi - sinPhi1 * cosPhi * cosLambda; - CosC = Math.Min(Math.Max(cosC, -1d), 1d); // protect against rounding errors - } - - public (double, double) RelativeScale(double radialScale, double perpendicularScale) - { - var scaleX = radialScale; - var scaleY = perpendicularScale; - - if (scaleX != scaleY) - { - var s = Math.Sqrt(X * X + Y * Y); - // sin and cos of azimuth from projection center, i.e. Atan2(Y/X) - var cos = X / s; - var sin = Y / s; - var x1 = scaleX * cos; - var y1 = scaleY * sin; - var x2 = scaleX * sin; - var y2 = scaleY * cos; - scaleX = Math.Sqrt(x1 * x1 + y1 * y1); - scaleY = Math.Sqrt(x2 * x2 + y2 * y2); - } - - return (scaleX, scaleY); - } - } - - protected ProjectedPoint GetProjectedPoint(double latitude, double longitude) - { - return new ProjectedPoint(Center.Latitude, Center.Longitude, latitude, longitude); - } - - protected Location GetLocation(double x, double y, double rho, double sinC) - { - var cos2C = 1d - sinC * sinC; - - if (cos2C < 0d) - { - return null; - } - - var cosC = Math.Sqrt(cos2C); - var phi1 = Center.Latitude * Math.PI / 180d; - var cosPhi1 = Math.Cos(phi1); - var sinPhi1 = Math.Sin(phi1); - var phi = Math.Asin(cosC * sinPhi1 + y * sinC * cosPhi1 / rho); // (20-14) - double u, v; - - if (Center.Latitude == 90d) // (20-16) - { - u = x; - v = -y; - } - else if (Center.Latitude == -90d) // (20-17) - { - u = x; - v = y; - } - else // (20-15) - { - u = x * sinC; - v = rho * cosPhi1 * cosC - y * sinPhi1 * sinC; - } - - return new Location( - phi * 180d / Math.PI, - Math.Atan2(u, v) * 180d / Math.PI + Center.Longitude); - } - } -} diff --git a/MapControl/Shared/EquirectangularProjection.cs b/MapControl/Shared/EquirectangularProjection.cs index 6e4fea1b..5946ed9d 100644 --- a/MapControl/Shared/EquirectangularProjection.cs +++ b/MapControl/Shared/EquirectangularProjection.cs @@ -33,7 +33,7 @@ namespace MapControl return new Matrix(1d / Math.Cos(latitude * Math.PI / 180d), 0d, 0d, 1d, 0d, 0d); } - public override Point? LocationToMap(double latitude, double longitude) + public override Point LocationToMap(double latitude, double longitude) { return new Point(MeterPerDegree * longitude, MeterPerDegree * latitude); } diff --git a/MapControl/Shared/GnomonicProjection.cs b/MapControl/Shared/GnomonicProjection.cs deleted file mode 100644 index c7823425..00000000 --- a/MapControl/Shared/GnomonicProjection.cs +++ /dev/null @@ -1,61 +0,0 @@ -using System; -#if WPF -using System.Windows; -using System.Windows.Media; -#elif AVALONIA -using Avalonia; -#endif - -namespace MapControl -{ - /// - /// Spherical Gnomonic Projection - AUTO2:97001. - /// See "Map Projections - A Working Manual" (https://pubs.usgs.gov/publication/pp1395), p.165-167. - /// - public class GnomonicProjection : AzimuthalProjection - { - public const string DefaultCrsId = "AUTO2:97001"; // GeoServer non-standard CRS identifier - - public GnomonicProjection() // parameterless constructor for XAML - : this(DefaultCrsId) - { - } - - public GnomonicProjection(string crsId) - { - CrsId = crsId; - } - - public override Matrix RelativeTransform(double latitude, double longitude) - { - var p = GetProjectedPoint(latitude, longitude); - var k = 1d / p.CosC; // p.165 (22-3) - var h = k * k; // p.165 (22-2) - (var scaleX, var scaleY) = p.RelativeScale(h, k); - - return RelativeTransform(latitude, longitude, scaleX, scaleY); - } - - public override Point? LocationToMap(double latitude, double longitude) - { - var p = GetProjectedPoint(latitude, longitude); - - if (p.CosC <= 0d) // p.167 "If cos c is zero or negative, the point is to be rejected." - { - return null; - } - - var k = 1d / p.CosC; // p.165 (22-3) - - return new Point(EquatorialRadius * k * p.X, EquatorialRadius * k * p.Y); // p.165 (22-4/5) - } - - public override Location MapToLocation(double x, double y) - { - var rho = Math.Sqrt(x * x + y * y); - var c = Math.Atan(rho / EquatorialRadius); // p.167 (22-16) - - return GetLocation(x, y, rho, Math.Sin(c)); - } - } -} diff --git a/MapControl/Shared/MapBase.cs b/MapControl/Shared/MapBase.cs index e83d4c3e..20d0c317 100644 --- a/MapControl/Shared/MapBase.cs +++ b/MapControl/Shared/MapBase.cs @@ -43,10 +43,6 @@ namespace MapControl DependencyPropertyHelper.Register(nameof(MapProjection), new WebMercatorProjection(), (map, oldValue, newValue) => map.MapProjectionPropertyChanged(newValue)); - public static readonly DependencyProperty ProjectionCenterProperty = - DependencyPropertyHelper.Register(nameof(ProjectionCenter), null, - (map, oldValue, newValue) => map.ProjectionCenterPropertyChanged(newValue)); - private Location transformCenter; private Point viewCenter; private double centerLongitude; @@ -86,17 +82,6 @@ namespace MapControl set => SetValue(MapProjectionProperty, value); } - /// - /// Gets or sets a projection center e.g. for azimuthal projections. - /// If ProjectionCenter is null, the value of the Center property is used as projection center. - /// This behavior is overridden when the Center property of a MapProjection is set explicitly. - /// - public Location ProjectionCenter - { - get => (Location)GetValue(ProjectionCenterProperty); - set => SetValue(ProjectionCenterProperty, value); - } - /// /// Gets or sets the location of the center point of the map. /// @@ -172,8 +157,8 @@ namespace MapControl } /// - /// Gets the ViewTransform instance that is used to transform between projected - /// map coordinates and view coordinates. + /// Gets the ViewTransform instance that is used to transform between + /// projected map coordinates and view coordinates. /// public ViewTransform ViewTransform { get; } = new ViewTransform(); @@ -186,7 +171,6 @@ namespace MapControl var transform = MapProjection.RelativeTransform(latitude, longitude); transform.Scale(ViewTransform.Scale, ViewTransform.Scale); transform.Rotate(ViewTransform.Rotation); - return transform; } @@ -194,57 +178,27 @@ namespace MapControl /// Gets a transform Matrix from meters to view coordinates for scaling and rotating /// at the specified Location. /// - public Matrix GetMapToViewTransform(Location location) - { - return GetMapToViewTransform(location.Latitude, location.Longitude); - } + public Matrix GetMapToViewTransform(Location location) => GetMapToViewTransform(location.Latitude, location.Longitude); /// /// Transforms geographic coordinates to a Point in view coordinates. /// - public Point? LocationToView(double latitude, double longitude) - { - var point = MapProjection.LocationToMap(latitude, longitude); - - if (point.HasValue) - { - point = ViewTransform.MapToView(point.Value); - } - - return point; - } + public Point LocationToView(double latitude, double longitude) => ViewTransform.MapToView(MapProjection.LocationToMap(latitude, longitude)); /// /// Transforms a Location in geographic coordinates to a Point in view coordinates. /// - public Point? LocationToView(Location location) - { - return LocationToView(location.Latitude, location.Longitude); - } + public Point LocationToView(Location location) => LocationToView(location.Latitude, location.Longitude); /// /// Transforms a Point in view coordinates to a Location in geographic coordinates. /// - public Location ViewToLocation(Point point) - { - return MapProjection.MapToLocation(ViewTransform.ViewToMap(point)); - } - - /// - /// Gets a MapRect in projected map coordinates that covers a rectangle in view coordinates. - /// - public MapRect ViewToMapRect(Rect rect) - { - return new MapRect(ViewTransform.ViewToMapBounds(rect), MapProjection.Center); - } + public Location ViewToLocation(Point point) => MapProjection.MapToLocation(ViewTransform.ViewToMap(point)); /// /// Gets a BoundingBox in geographic coordinates that covers a rectangle in view coordinates. /// - public BoundingBox ViewToBoundingBox(Rect rect) - { - return MapProjection.MapToBoundingBox(ViewTransform.ViewToMapBounds(rect)); - } + public BoundingBox ViewToBoundingBox(Rect rect) => MapProjection.MapToBoundingBox(ViewTransform.ViewToMapBounds(rect)); /// /// Sets a temporary center point in view coordinates for scaling and rotation transformations. @@ -253,8 +207,8 @@ namespace MapControl /// public void SetTransformCenter(Point center) { + viewCenter = center; transformCenter = ViewToLocation(center); - viewCenter = transformCenter != null ? center : new Point(ActualWidth / 2d, ActualHeight / 2d); } /// @@ -262,8 +216,8 @@ namespace MapControl /// public void ResetTransformCenter() { - transformCenter = null; viewCenter = new Point(ActualWidth / 2d, ActualHeight / 2d); + transformCenter = null; } /// @@ -273,12 +227,7 @@ namespace MapControl { if (translation.X != 0d || translation.Y != 0d) { - var center = ViewToLocation(new Point(viewCenter.X - translation.X, viewCenter.Y - translation.Y)); - - if (center != null) - { - Center = center; - } + Center = ViewToLocation(new Point(viewCenter.X - translation.X, viewCenter.Y - translation.Y)); } } @@ -350,22 +299,11 @@ namespace MapControl /// public void ZoomToBounds(BoundingBox bounds) { - (var mapRect, var _) = MapProjection.BoundingBoxToMap(bounds); - - if (mapRect.HasValue) - { - var targetCenter = MapProjection.MapToLocation( - mapRect.Value.X + mapRect.Value.Width / 2d, - mapRect.Value.Y + mapRect.Value.Height / 2d); - - if (targetCenter != null) - { - var scale = Math.Min(ActualWidth / mapRect.Value.Width, ActualHeight / mapRect.Value.Height); - TargetZoomLevel = ScaleToZoomLevel(scale); - TargetCenter = targetCenter; - TargetHeading = 0d; - } - } + (var rect, var _) = MapProjection.BoundingBoxToMap(bounds); + var scale = Math.Min(ActualWidth / rect.Width, ActualHeight / rect.Height); + TargetZoomLevel = ScaleToZoomLevel(scale); + TargetCenter = new Location((bounds.South + bounds.North) / 2d, (bounds.West + bounds.East) / 2d); + TargetHeading = 0d; } internal bool InsideViewBounds(Point point) @@ -441,107 +379,65 @@ namespace MapControl if (projection.IsNormalCylindrical) { var maxLocation = projection.MapToLocation(0d, 180d * MapProjection.Wgs84MeterPerDegree); - - if (maxLocation != null && maxLocation.Latitude < 90d) - { - maxLatitude = maxLocation.Latitude; - Center = CoerceCenterProperty(Center); - } + maxLatitude = maxLocation.Latitude; + Center = CoerceCenterProperty(Center); } ResetTransformCenter(); UpdateTransform(false, true); } - private void ProjectionCenterPropertyChanged(Location center) - { - if (!internalPropertyChange) - { - if (center != null) - { - var longitude = Location.NormalizeLongitude(center.Longitude); - - if (!center.LongitudeEquals(longitude)) - { - SetValueInternal(ProjectionCenterProperty, new Location(center.Latitude, longitude)); - } - } - - ResetTransformCenter(); - UpdateTransform(); - } - } - private void UpdateTransform(bool resetTransformCenter = false, bool projectionChanged = false) { var transformCenterChanged = false; var viewScale = ZoomLevelToScale(ZoomLevel); - var projection = MapProjection; + var mapCenter = MapProjection.LocationToMap(transformCenter ?? Center); - projection.SetCenter(ProjectionCenter ?? Center); + ViewTransform.SetTransform(mapCenter, viewCenter, viewScale, -Heading); - var mapCenter = projection.LocationToMap(transformCenter ?? Center); - - if (mapCenter.HasValue) + if (transformCenter != null) { - ViewTransform.SetTransform(mapCenter.Value, viewCenter, viewScale, -Heading); + var center = ViewToLocation(new Point(ActualWidth / 2d, ActualHeight / 2d)); + var latitude = center.Latitude; + var longitude = Location.NormalizeLongitude(center.Longitude); - if (transformCenter != null) + if (latitude < -maxLatitude || latitude > maxLatitude) { - var center = ViewToLocation(new Point(ActualWidth / 2d, ActualHeight / 2d)); - - if (center != null) - { - var latitude = center.Latitude; - var longitude = Location.NormalizeLongitude(center.Longitude); - - if (latitude < -maxLatitude || latitude > maxLatitude) - { - latitude = Math.Min(Math.Max(latitude, -maxLatitude), maxLatitude); - resetTransformCenter = true; - } - - if (!center.Equals(latitude, longitude)) - { - center = new Location(latitude, longitude); - } - - SetValueInternal(CenterProperty, center); - - if (centerAnimation == null) - { - SetValueInternal(TargetCenterProperty, center); - } - - if (resetTransformCenter) - { - // Check if transform center has moved across 180° longitude. - // - transformCenterChanged = Math.Abs(center.Longitude - transformCenter.Longitude) > 180d; - - ResetTransformCenter(); - - projection.SetCenter(ProjectionCenter ?? Center); - - mapCenter = projection.LocationToMap(center); - - if (mapCenter.HasValue) - { - ViewTransform.SetTransform(mapCenter.Value, viewCenter, viewScale, -Heading); - } - } - } + latitude = Math.Min(Math.Max(latitude, -maxLatitude), maxLatitude); + resetTransformCenter = true; } - ViewScale = ViewTransform.Scale; + if (!center.Equals(latitude, longitude)) + { + center = new Location(latitude, longitude); + } - // Check if view center has moved across 180° longitude. - // - transformCenterChanged = transformCenterChanged || Math.Abs(Center.Longitude - centerLongitude) > 180d; - centerLongitude = Center.Longitude; + SetValueInternal(CenterProperty, center); - OnViewportChanged(new ViewportChangedEventArgs(projectionChanged, transformCenterChanged)); + if (centerAnimation == null) + { + SetValueInternal(TargetCenterProperty, center); + } + + if (resetTransformCenter) + { + // Check if transform center has moved across 180° longitude. + // + transformCenterChanged = Math.Abs(center.Longitude - transformCenter.Longitude) > 180d; + ResetTransformCenter(); + mapCenter = MapProjection.LocationToMap(center); + ViewTransform.SetTransform(mapCenter, viewCenter, viewScale, -Heading); + } } + + ViewScale = ViewTransform.Scale; + + // Check if view center has moved across 180° longitude. + // + transformCenterChanged = transformCenterChanged || Math.Abs(Center.Longitude - centerLongitude) > 180d; + centerLongitude = Center.Longitude; + + OnViewportChanged(new ViewportChangedEventArgs(projectionChanged, transformCenterChanged)); } protected override void OnViewportChanged(ViewportChangedEventArgs e) diff --git a/MapControl/Shared/MapBorderPanel.cs b/MapControl/Shared/MapBorderPanel.cs index f99dc0b0..9170d667 100644 --- a/MapControl/Shared/MapBorderPanel.cs +++ b/MapControl/Shared/MapBorderPanel.cs @@ -35,7 +35,7 @@ namespace MapControl return (bool)element.GetValue(OnBorderProperty); } - protected override void SetViewPosition(FrameworkElement element, ref Point? position) + protected override Point SetViewPosition(FrameworkElement element, Point position) { var onBorder = false; var w = ParentMap.ActualWidth; @@ -45,12 +45,11 @@ namespace MapControl var maxX = w - BorderWidth / 2d; var maxY = h - BorderWidth / 2d; - if (position.HasValue && - (position.Value.X < minX || position.Value.X > maxX || - position.Value.Y < minY || position.Value.Y > maxY)) + if (position.X < minX || position.X > maxX || + position.Y < minY || position.Y > maxY) { - var dx = position.Value.X - w / 2d; - var dy = position.Value.Y - h / 2d; + var dx = position.X - w / 2d; + var dy = position.Y - h / 2d; var cx = (maxX - minX) / 2d; var cy = (maxY - minY) / 2d; double x, y; @@ -86,7 +85,7 @@ namespace MapControl element.SetValue(OnBorderProperty, onBorder); - base.SetViewPosition(element, ref position); + return base.SetViewPosition(element, position); } } } diff --git a/MapControl/Shared/MapGraticule.cs b/MapControl/Shared/MapGraticule.cs index af18927d..a166b0da 100644 --- a/MapControl/Shared/MapGraticule.cs +++ b/MapControl/Shared/MapGraticule.cs @@ -127,10 +127,7 @@ namespace MapControl // var pos = ParentMap.LocationToView(latitude, longitude + 10d / PixelPerDegree); - if (pos.HasValue) - { - rotation = Math.Atan2(pos.Value.Y - position.Y, pos.Value.X - position.X) * 180d / Math.PI; - } + rotation = Math.Atan2(pos.Y - position.Y, pos.X - position.X) * 180d / Math.PI; } if (rotation.HasValue) @@ -173,31 +170,19 @@ namespace MapControl { var p1 = ParentMap.LocationToView(lat, boundingBox.West); var p2 = ParentMap.LocationToView(lat, boundingBox.East); - - if (p1.HasValue && p2.HasValue) - { - figures.Add(CreateLineFigure(p1.Value, p2.Value)); - } + figures.Add(CreateLineFigure(p1, p2)); } for (var lon = lonLabelStart; lon <= boundingBox.East; lon += lineDistance) { var p1 = ParentMap.LocationToView(boundingBox.South, lon); var p2 = ParentMap.LocationToView(boundingBox.North, lon); - - if (p1.HasValue && p2.HasValue) - { - figures.Add(CreateLineFigure(p1.Value, p2.Value)); - } + figures.Add(CreateLineFigure(p1, p2)); for (var lat = latLabelStart; lat <= boundingBox.North; lat += lineDistance) { var position = ParentMap.LocationToView(lat, lon); - - if (position.HasValue) - { - AddLabel(labels, lat, lon, position.Value, ParentMap.ViewTransform.Rotation); - } + AddLabel(labels, lat, lon, position, ParentMap.ViewTransform.Rotation); } } } @@ -210,7 +195,6 @@ namespace MapControl var interpolationCount = Math.Max(1, (int)Math.Ceiling(lineDistance / LineInterpolationResolution)); var interpolationDistance = lineDistance / interpolationCount; var latPoints = latSegments * interpolationCount; - var centerLon = Math.Round(ParentMap.Center.Longitude / lineDistance) * lineDistance; var minLon = centerLon - lineDistance; var maxLon = centerLon + lineDistance; @@ -239,11 +223,8 @@ namespace MapControl var points = new List(); var position = ParentMap.LocationToView(lat, lon); - if (position.HasValue) - { - points.Add(position.Value); - AddLabel(labels, lat, lon, position.Value); - } + points.Add(position); + AddLabel(labels, lat, lon, position); for (int j = 0; j < lonSegments; j++) { @@ -251,17 +232,10 @@ namespace MapControl { lon = minLon + j * lineDistance + k * interpolationDistance; position = ParentMap.LocationToView(lat, lon); - - if (position.HasValue) - { - points.Add(position.Value); - } + points.Add(position); } - if (position.HasValue) - { - AddLabel(labels, lat, lon, position.Value); - } + AddLabel(labels, lat, lon, position); } if (points.Count >= 2) @@ -281,14 +255,11 @@ namespace MapControl { var p = ParentMap.LocationToView(startLatitude + i * deltaLatitude, longitude); - if (p.HasValue) - { - visible = visible || - p.Value.X >= 0d && p.Value.X <= ParentMap.ActualWidth && - p.Value.Y >= 0d && p.Value.Y <= ParentMap.ActualHeight; + visible = visible || + p.X >= 0d && p.X <= ParentMap.ActualWidth && + p.Y >= 0d && p.Y <= ParentMap.ActualHeight; - points.Add(p.Value); - } + points.Add(p); } if (points.Count >= 2) diff --git a/MapControl/Shared/MapImageLayer.cs b/MapControl/Shared/MapImageLayer.cs index 6e3117ad..a7edd1e6 100644 --- a/MapControl/Shared/MapImageLayer.cs +++ b/MapControl/Shared/MapImageLayer.cs @@ -180,7 +180,7 @@ namespace MapControl updateTimer.Stop(); ImageSource image = null; - MapRect mapRect = null; + Rect? mapRect = null; if (ParentMap != null && (SupportedCrsIds == null || SupportedCrsIds.Contains(ParentMap.MapProjection.CrsId))) @@ -193,8 +193,8 @@ namespace MapControl var x = (ParentMap.ActualWidth - width) / 2d; var y = (ParentMap.ActualHeight - height) / 2d; - mapRect = ParentMap.ViewToMapRect(new Rect(x, y, width, height)); - image = await GetImageAsync(mapRect.Rect, loadingProgress); + mapRect = ParentMap.ViewTransform.ViewToMapBounds(new Rect(x, y, width, height)); + image = await GetImageAsync(mapRect.Value, loadingProgress); } } @@ -216,7 +216,7 @@ namespace MapControl } } - private void SwapImages(ImageSource image, MapRect mapRect) + private void SwapImages(ImageSource image, Rect? mapRect) { if (Children.Count >= 2) { diff --git a/MapControl/Shared/MapPanel.cs b/MapControl/Shared/MapPanel.cs index 01aad839..8aca1be3 100644 --- a/MapControl/Shared/MapPanel.cs +++ b/MapControl/Shared/MapPanel.cs @@ -131,15 +131,15 @@ namespace MapControl /// /// Gets the MapRect of an element. /// - public static MapRect GetMapRect(FrameworkElement element) + public static Rect? GetMapRect(FrameworkElement element) { - return (MapRect)element.GetValue(MapRectProperty); + return (Rect?)element.GetValue(MapRectProperty); } /// /// Sets the MapRect of an element. /// - public static void SetMapRect(FrameworkElement element, MapRect value) + public static void SetMapRect(FrameworkElement element, Rect? value) { element.SetValue(MapRectProperty, value); } @@ -157,9 +157,11 @@ namespace MapControl /// ArrangeOverride and may be overridden to modify the actual view position value. /// An overridden method should call this method to set the attached property. /// - protected virtual void SetViewPosition(FrameworkElement element, ref Point? position) + protected virtual Point SetViewPosition(FrameworkElement element, Point position) { element.SetValue(ViewPositionProperty, position); + + return position; } protected virtual void SetParentMap(MapBase map) @@ -214,19 +216,17 @@ namespace MapControl return finalSize; } - private Point? GetViewPosition(Location location) + private Point GetViewPosition(Location location) { var position = parentMap.LocationToView(location); - if (parentMap.MapProjection.IsNormalCylindrical && - position.HasValue && !parentMap.InsideViewBounds(position.Value)) + if (parentMap.MapProjection.IsNormalCylindrical && !parentMap.InsideViewBounds(position)) { - var nearestPosition = parentMap.LocationToView( - location.Latitude, parentMap.NearestLongitude(location.Longitude)); + var longitude = parentMap.NearestLongitude(location.Longitude); - if (nearestPosition.HasValue) + if (longitude != location.Longitude) { - position = nearestPosition; + position = parentMap.LocationToView(location.Latitude, longitude); } } @@ -238,20 +238,14 @@ namespace MapControl var center = new Point(mapRect.X + mapRect.Width / 2d, mapRect.Y + mapRect.Height / 2d); var position = parentMap.ViewTransform.MapToView(center); - if (parentMap.MapProjection.IsNormalCylindrical && - !parentMap.InsideViewBounds(position)) + if (parentMap.MapProjection.IsNormalCylindrical && !parentMap.InsideViewBounds(position)) { var location = parentMap.MapProjection.MapToLocation(center); + var longitude = parentMap.NearestLongitude(location.Longitude); - if (location != null) + if (longitude != location.Longitude) { - var nearestPosition = parentMap.LocationToView( - location.Latitude, parentMap.NearestLongitude(location.Longitude)); - - if (nearestPosition.HasValue) - { - position = nearestPosition.Value; - } + position = parentMap.LocationToView(location.Latitude, longitude); } } @@ -269,19 +263,14 @@ namespace MapControl if (location != null) { - var position = GetViewPosition(location); - - SetViewPosition(element, ref position); + var position = SetViewPosition(element, GetViewPosition(location)); if (GetAutoCollapse(element)) { - element.SetVisible(position.HasValue && parentMap.InsideViewBounds(position.Value)); + element.SetVisible(parentMap.InsideViewBounds(position)); } - if (position.HasValue) - { - ArrangeElement(element, position.Value); - } + ArrangeElement(element, position); } else { @@ -289,11 +278,9 @@ namespace MapControl var mapRect = GetMapRect(element); - if (mapRect != null) + if (mapRect.HasValue) { - mapRect.Update(parentMap.MapProjection); - - ArrangeElement(element, mapRect.Rect, null); + ArrangeElement(element, mapRect.Value, null); } else { @@ -303,10 +290,7 @@ namespace MapControl { (var rect, var transform) = parentMap.MapProjection.BoundingBoxToMap(boundingBox); - if (rect.HasValue) - { - ArrangeElement(element, rect.Value, transform); - } + ArrangeElement(element, rect, transform); } else { diff --git a/MapControl/Shared/MapPath.cs b/MapControl/Shared/MapPath.cs index 1091a8f3..29873d7e 100644 --- a/MapControl/Shared/MapPath.cs +++ b/MapControl/Shared/MapPath.cs @@ -80,7 +80,7 @@ namespace MapControl { var position = ParentMap.LocationToView(location); - if (position.HasValue && !ParentMap.InsideViewBounds(position.Value)) + if (!ParentMap.InsideViewBounds(position)) { longitudeOffset = ParentMap.NearestLongitude(location.Longitude) - location.Longitude; } @@ -89,35 +89,25 @@ namespace MapControl return longitudeOffset; } - protected Point? LocationToMap(Location location, double longitudeOffset) + protected Point LocationToMap(Location location, double longitudeOffset) { var point = ParentMap.MapProjection.LocationToMap(location.Latitude, location.Longitude + longitudeOffset); - if (point.HasValue) + if (point.Y == double.PositiveInfinity) { - if (point.Value.Y == double.PositiveInfinity) - { - point = new Point(point.Value.X, 1e9); - } - else if (point.Value.Y == double.NegativeInfinity) - { - point = new Point(point.Value.X, -1e9); - } + point = new Point(point.X, 1e9); + } + else if (point.Y == double.NegativeInfinity) + { + point = new Point(point.X, -1e9); } return point; } - protected Point? LocationToView(Location location, double longitudeOffset) + protected Point LocationToView(Location location, double longitudeOffset) { - var point = LocationToMap(location, longitudeOffset); - - if (point.HasValue) - { - point = ParentMap.ViewTransform.MapToView(point.Value); - } - - return point; + return ParentMap.ViewTransform.MapToView(LocationToMap(location, longitudeOffset)); } } } diff --git a/MapControl/Shared/MapProjection.cs b/MapControl/Shared/MapProjection.cs index 9b6bf52e..4ae4001a 100644 --- a/MapControl/Shared/MapProjection.cs +++ b/MapControl/Shared/MapProjection.cs @@ -55,54 +55,6 @@ namespace MapControl public double MeterPerDegree => EquatorialRadius * Math.PI / 180d; - private Location center; - private bool updateCenter; - - /// - /// Gets or sets an optional projection center. If the property is set to a non-null value, - /// it overrides the projection center set by MapBase.Center or MapBase.ProjectionCenter. - /// - public Location Center - { - get => center; - set - { - updateCenter = true; - - if (value != null) - { - var longitude = Location.NormalizeLongitude(value.Longitude); - - SetCenter(value.LongitudeEquals(longitude) ? value : new Location(value.Latitude, longitude)); - - updateCenter = false; - } - } - } - - protected void EnableCenterUpdates() - { - updateCenter = true; - SetCenter(new Location()); - } - - /// - /// Called by MapBase.UpdateTransform(). - /// - internal void SetCenter(Location value) - { - if (updateCenter) - { - if (center == null || !center.Equals(value)) - { - center = value; - CenterChanged(); - } - } - } - - protected virtual void CenterChanged() { } - /// /// Gets the relative transform at the specified geographic coordinates. /// The returned Matrix represents the local distortion of the map projection. @@ -111,13 +63,11 @@ namespace MapControl /// /// Transforms geographic coordinates to a Point in projected map coordinates. - /// Returns null when the location can not be transformed. /// - public abstract Point? LocationToMap(double latitude, double longitude); + public abstract Point LocationToMap(double latitude, double longitude); /// /// Transforms projected map coordinates to a Location in geographic coordinates. - /// Returns null when the coordinates can not be transformed. /// public abstract Location MapToLocation(double x, double y); @@ -128,51 +78,40 @@ namespace MapControl /// /// Transforms a Location in geographic coordinates to a Point in projected map coordinates. - /// Returns null when the Location can not be transformed. /// - public Point? LocationToMap(Location location) => LocationToMap(location.Latitude, location.Longitude); + public Point LocationToMap(Location location) => LocationToMap(location.Latitude, location.Longitude); /// /// Transforms a Point in projected map coordinates to a Location in geographic coordinates. - /// Returns null when the Point can not be transformed. /// public Location MapToLocation(Point point) => MapToLocation(point.X, point.Y); /// - /// Transforms a BoundingBox in geographic coordinates to a Rect in projected map coordinates with - /// an optional transform Matrix. Returns (null, null) when the BoundingBox can not be transformed. + /// Transforms a BoundingBox in geographic coordinates to a Rect in projected map coordinates + /// with an optional transform Matrix. /// - public (Rect?, Matrix?) BoundingBoxToMap(BoundingBox boundingBox) + public (Rect, Matrix?) BoundingBoxToMap(BoundingBox boundingBox) { - Rect? rect = null; + Rect rect; Matrix? transform = null; - var sw = LocationToMap(boundingBox.South, boundingBox.West); - var se = LocationToMap(boundingBox.South, boundingBox.East); - var nw = LocationToMap(boundingBox.North, boundingBox.West); - var ne = LocationToMap(boundingBox.North, boundingBox.East); - if (sw.HasValue && se.HasValue && nw.HasValue && ne.HasValue) + if (IsNormalCylindrical) { - var south = new Point((sw.Value.X + se.Value.X) / 2d, (sw.Value.Y + se.Value.Y) / 2d); // south midpoint - var north = new Point((nw.Value.X + ne.Value.X) / 2d, (nw.Value.Y + ne.Value.Y) / 2d); // north midpoint - var west = new Point((nw.Value.X + sw.Value.X) / 2d, (nw.Value.Y + sw.Value.Y) / 2d); // west midpoint - var east = new Point((ne.Value.X + se.Value.X) / 2d, (ne.Value.Y + se.Value.Y) / 2d); // east midpoint - var center = new Point((west.X + east.X) / 2d, (west.Y + east.Y) / 2d); // midpoint of segment west-east - var dx1 = east.X - west.X; - var dy1 = east.Y - west.Y; - var dx2 = north.X - south.X; - var dy2 = north.Y - south.Y; - var width = Math.Sqrt(dx1 * dx1 + dy1 * dy1); // distance west-east - var height = Math.Sqrt(dx2 * dx2 + dy2 * dy2); // distance south-north + var southWest = LocationToMap(boundingBox.South, boundingBox.West); + var northEast = LocationToMap(boundingBox.North, boundingBox.East); + + rect = new Rect(southWest.X, southWest.Y, northEast.X - southWest.X, northEast.Y - southWest.Y); + } + else + { + var latitude = (boundingBox.South + boundingBox.North) / 2d; + var longitude = (boundingBox.West + boundingBox.East) / 2d; + var center = LocationToMap(latitude, longitude); + var width = MeterPerDegree * (boundingBox.East - boundingBox.West) * Math.Cos(latitude * Math.PI / 180d); + var height = MeterPerDegree * (boundingBox.North - boundingBox.South); rect = new Rect(center.X - width / 2d, center.Y - height / 2d, width, height); - - if (dy1 != 0d || dx2 != 0d) - { - // Skew matrix with skewX = Atan(-dx2 / dy2) and skewY = Atan(-dy1 / dx1). - // - transform = new Matrix(1d, -dy1 / dx1, -dx2 / dy2, 1d, 0d, 0d); - } + transform = RelativeTransform(latitude, longitude); } return (rect, transform); @@ -180,44 +119,18 @@ namespace MapControl /// /// Transforms a Rect in projected map coordinates to a BoundingBox in geographic coordinates. - /// Returns null when the Rect can not be transformed. /// public BoundingBox MapToBoundingBox(Rect rect) { - var sw = MapToLocation(rect.X, rect.Y); - var ne = MapToLocation(rect.X + rect.Width, rect.Y + rect.Height); + var southWest = MapToLocation(rect.X, rect.Y); + var northEast = MapToLocation(rect.X + rect.Width, rect.Y + rect.Height); - return sw != null && ne != null ? new BoundingBox(sw.Latitude, sw.Longitude, ne.Latitude, ne.Longitude) : null; + return new BoundingBox(southWest.Latitude, southWest.Longitude, northEast.Latitude, northEast.Longitude); } public override string ToString() { return CrsId; } - - /// - /// Used by azimuthal projections, where local skewing can not be directly calculated. - /// - protected Matrix RelativeTransform(double latitude, double longitude, double scaleX, double scaleY) - { - var north = LocationToMap(latitude - 1e-3, longitude); - var south = LocationToMap(latitude + 1e-3, longitude); - var west = LocationToMap(latitude, longitude - 1e-3); - var east = LocationToMap(latitude, longitude + 1e-3); - var tanSkewX = 0d; - var tanSkewY = 0d; - - if (north.HasValue && south.HasValue && west.HasValue && east.HasValue) - { - var dx1 = east.Value.X - west.Value.X; - var dy1 = east.Value.Y - west.Value.Y; - var dx2 = north.Value.X - south.Value.X; - var dy2 = north.Value.Y - south.Value.Y; - tanSkewX = -dx2 / dy2; - tanSkewY = -dy1 / dx1; - } - - return new Matrix(scaleX, scaleX * tanSkewY, scaleY * tanSkewX, scaleY, 0d, 0d); - } } } diff --git a/MapControl/Shared/MapProjectionFactory.cs b/MapControl/Shared/MapProjectionFactory.cs index 8519f9af..c726614f 100644 --- a/MapControl/Shared/MapProjectionFactory.cs +++ b/MapControl/Shared/MapProjectionFactory.cs @@ -13,12 +13,6 @@ namespace MapControl EquirectangularProjection.DefaultCrsId or "CRS:84" => new EquirectangularProjection(crsId), Wgs84UpsNorthProjection.DefaultCrsId => new Wgs84UpsNorthProjection(), Wgs84UpsSouthProjection.DefaultCrsId => new Wgs84UpsSouthProjection(), - Wgs84AutoUtmProjection.DefaultCrsId => new Wgs84AutoUtmProjection(), - Wgs84AutoTmProjection.DefaultCrsId => new Wgs84AutoTmProjection(), - OrthographicProjection.DefaultCrsId => new OrthographicProjection(), - GnomonicProjection.DefaultCrsId => new GnomonicProjection(), - StereographicProjection.DefaultCrsId => new StereographicProjection(), - AzimuthalEquidistantProjection.DefaultCrsId => new AzimuthalEquidistantProjection(), _ => GetProjectionFromEpsgCode(crsId), }; diff --git a/MapControl/Shared/MapRect.cs b/MapControl/Shared/MapRect.cs deleted file mode 100644 index 099d82e0..00000000 --- a/MapControl/Shared/MapRect.cs +++ /dev/null @@ -1,27 +0,0 @@ -#if WPF -using System.Windows; -#elif AVALONIA -using Avalonia; -#endif - -namespace MapControl -{ - public class MapRect(Rect rect, Location origin) - { - public Rect Rect { get; private set; } = rect; - public Location Origin { get; private set; } = origin; - - public void Update(MapProjection projection) - { - Point? origin; - - if (Origin != null && projection.Center != null && - !Origin.Equals(projection.Center) && - (origin = projection.LocationToMap(Origin)).HasValue) - { - Rect = new Rect(Rect.X + origin.Value.X, Rect.Y + origin.Value.Y, Rect.Width, Rect.Height); - Origin = projection.Center; - } - } - } -} diff --git a/MapControl/Shared/MapScale.cs b/MapControl/Shared/MapScale.cs index 41f94edc..4a6d3bc5 100644 --- a/MapControl/Shared/MapScale.cs +++ b/MapControl/Shared/MapScale.cs @@ -92,35 +92,31 @@ namespace MapControl var y0 = ParentMap.ActualHeight / 2d; var p1 = ParentMap.ViewToLocation(new Point(x0 - 50d, y0)); var p2 = ParentMap.ViewToLocation(new Point(x0 + 50d, y0)); + var scale = 100d / p1.GetDistance(p2); + var length = MinWidth / scale; + var magnitude = Math.Pow(10d, Math.Floor(Math.Log10(length))); - if (p1 != null && p2 != null) - { - var scale = 100d / p1.GetDistance(p2); - var length = MinWidth / scale; - var magnitude = Math.Pow(10d, Math.Floor(Math.Log10(length))); + length = length / magnitude < 2d ? 2d * magnitude + : length / magnitude < 5d ? 5d * magnitude + : 10d * magnitude; - length = length / magnitude < 2d ? 2d * magnitude - : length / magnitude < 5d ? 5d * magnitude - : 10d * magnitude; + size = new Size( + length * scale + StrokeThickness + Padding.Left + Padding.Right, + 1.5 * label.FontSize + 2 * StrokeThickness + Padding.Top + Padding.Bottom); - size = new Size( - length * scale + StrokeThickness + Padding.Left + Padding.Right, - 1.5 * label.FontSize + 2 * StrokeThickness + Padding.Top + Padding.Bottom); + var x1 = Padding.Left + StrokeThickness / 2d; + var x2 = size.Width - Padding.Right - StrokeThickness / 2d; + var y1 = size.Height / 2d; + var y2 = size.Height - Padding.Bottom - StrokeThickness / 2d; - var x1 = Padding.Left + StrokeThickness / 2d; - var x2 = size.Width - Padding.Right - StrokeThickness / 2d; - var y1 = size.Height / 2d; - var y2 = size.Height - Padding.Bottom - StrokeThickness / 2d; + line.Points = [new Point(x1, y1), new Point(x1, y2), new Point(x2, y2), new Point(x2, y1)]; - line.Points = [new Point(x1, y1), new Point(x1, y2), new Point(x2, y2), new Point(x2, y1)]; + label.Text = length >= 1000d + ? string.Format(CultureInfo.InvariantCulture, "{0:F0} km", length / 1000d) + : string.Format(CultureInfo.InvariantCulture, "{0:F0} m", length); - label.Text = length >= 1000d - ? string.Format(CultureInfo.InvariantCulture, "{0:F0} km", length / 1000d) - : string.Format(CultureInfo.InvariantCulture, "{0:F0} m", length); - - line.Measure(size); - label.Measure(size); - } + line.Measure(size); + label.Measure(size); } return size; diff --git a/MapControl/Shared/OrthographicProjection.cs b/MapControl/Shared/OrthographicProjection.cs deleted file mode 100644 index 7750f285..00000000 --- a/MapControl/Shared/OrthographicProjection.cs +++ /dev/null @@ -1,57 +0,0 @@ -using System; -#if WPF -using System.Windows; -using System.Windows.Media; -#elif AVALONIA -using Avalonia; -#endif - -namespace MapControl -{ - /// - /// Spherical Orthographic Projection - AUTO2:42003. - /// See "Map Projections - A Working Manual" (https://pubs.usgs.gov/publication/pp1395), p.148-150. - /// - public class OrthographicProjection : AzimuthalProjection - { - public const string DefaultCrsId = "AUTO2:42003"; - - public OrthographicProjection() // parameterless constructor for XAML - : this(DefaultCrsId) - { - } - - public OrthographicProjection(string crsId) - { - CrsId = crsId; - } - - public override Matrix RelativeTransform(double latitude, double longitude) - { - var p = GetProjectedPoint(latitude, longitude); - (var scaleX, var scaleY) = p.RelativeScale(p.CosC, 1d); // p.149 (20-5), k == 1 - - return RelativeTransform(latitude, longitude, scaleX, scaleY); - } - - public override Point? LocationToMap(double latitude, double longitude) - { - var p = GetProjectedPoint(latitude, longitude); - - if (p.CosC < 0d) // p.149 "If cos c is negative, the point is not to be plotted." - { - return null; - } - - return new Point(EquatorialRadius * p.X, EquatorialRadius * p.Y); // p.149 (20-3/4) - } - - public override Location MapToLocation(double x, double y) - { - var rho = Math.Sqrt(x * x + y * y); - var sinC = rho / EquatorialRadius; // p.150 (20-19) - - return GetLocation(x, y, rho, sinC); - } - } -} diff --git a/MapControl/Shared/PolarStereographicProjection.cs b/MapControl/Shared/PolarStereographicProjection.cs index 6b12b2d9..ac9e4dcd 100644 --- a/MapControl/Shared/PolarStereographicProjection.cs +++ b/MapControl/Shared/PolarStereographicProjection.cs @@ -21,7 +21,7 @@ namespace MapControl public double FalseNorthing { get; set; } = 2e6; public Hemisphere Hemisphere { get; set; } - public static double RelativeScale(Hemisphere hemisphere, double flattening, double scaleFactor, double latitude) + public static Matrix RelativeScale(Hemisphere hemisphere, double flattening, double scaleFactor, double latitude, double longitude) { var sign = hemisphere == Hemisphere.North ? 1d : -1d; var phi = sign * latitude * Math.PI / 180d; @@ -33,20 +33,21 @@ namespace MapControl / Math.Pow((1d - eSinPhi) / (1d + eSinPhi), e / 2d); // p.161 (15-9) // r == ρ/a - var r = scaleFactor * 2d * t / Math.Sqrt(Math.Pow(1d + e, 1d + e) * Math.Pow(1d - e, 1d - e)); // p.161 (21-33) + var r = 2d * scaleFactor * t / Math.Sqrt(Math.Pow(1d + e, 1d + e) * Math.Pow(1d - e, 1d - e)); // p.161 (21-33) var m = Math.Cos(phi) / Math.Sqrt(1d - eSinPhi * eSinPhi); // p.160 (14-15) + var k = r / m; // p.161 (21-32) - return r / m; // p.161 (21-32) + var transform = new Matrix(k, 0d, 0d, k, 0d, 0d); + transform.Rotate(-sign * longitude); + return transform; } public override Matrix RelativeTransform(double latitude, double longitude) { - var k = RelativeScale(Hemisphere, Flattening, ScaleFactor, latitude); - - return RelativeTransform(latitude, longitude, k, k); + return RelativeScale(Hemisphere, Flattening, ScaleFactor, latitude, longitude); } - public override Point? LocationToMap(double latitude, double longitude) + public override Point LocationToMap(double latitude, double longitude) { var sign = Hemisphere == Hemisphere.North ? 1d : -1d; var phi = sign * latitude * Math.PI / 180d; diff --git a/MapControl/Shared/StereographicProjection.cs b/MapControl/Shared/StereographicProjection.cs deleted file mode 100644 index 84d0065f..00000000 --- a/MapControl/Shared/StereographicProjection.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System; -#if WPF -using System.Windows; -using System.Windows.Media; -#elif AVALONIA -using Avalonia; -#endif - -namespace MapControl -{ - /// - /// Spherical Stereographic Projection - AUTO2:97002. - /// See "Map Projections - A Working Manual" (https://pubs.usgs.gov/publication/pp1395), p.157-160. - /// - public class StereographicProjection : AzimuthalProjection - { - public const string DefaultCrsId = "AUTO2:97002"; // GeoServer non-standard CRS identifier - - public StereographicProjection() // parameterless constructor for XAML - : this(DefaultCrsId) - { - } - - public StereographicProjection(string crsId) - { - CrsId = crsId; - } - - public override Matrix RelativeTransform(double latitude, double longitude) - { - var p = GetProjectedPoint(latitude, longitude); - var k = 2d / (1d + p.CosC); // p.157 (21-4), k0 == 1 - - return RelativeTransform(latitude, longitude, k, k); - } - - public override Point? LocationToMap(double latitude, double longitude) - { - var p = GetProjectedPoint(latitude, longitude); - var k = 2d / (1d + p.CosC); // p.157 (21-4), k0 == 1 - - return new Point(EquatorialRadius * k * p.X, EquatorialRadius * k * p.Y); // p.157 (21-2/3) - } - - public override Location MapToLocation(double x, double y) - { - var rho = Math.Sqrt(x * x + y * y); - var c = 2d * Math.Atan(rho / (2d * EquatorialRadius)); // p.159 (21-15), k0 == 1 - - return GetLocation(x, y, rho, Math.Sin(c)); - } - } -} diff --git a/MapControl/Shared/TransverseMercatorProjection.cs b/MapControl/Shared/TransverseMercatorProjection.cs index 70f12fc3..387967f0 100644 --- a/MapControl/Shared/TransverseMercatorProjection.cs +++ b/MapControl/Shared/TransverseMercatorProjection.cs @@ -100,7 +100,7 @@ namespace MapControl return transform; } - public override Point? LocationToMap(double latitude, double longitude) + public override Point LocationToMap(double latitude, double longitude) { // φ var phi = latitude * Math.PI / 180d; diff --git a/MapControl/Shared/TransverseMercatorProjectionSnyder.cs b/MapControl/Shared/TransverseMercatorProjectionSnyder.cs index 07bfd337..3bb06244 100644 --- a/MapControl/Shared/TransverseMercatorProjectionSnyder.cs +++ b/MapControl/Shared/TransverseMercatorProjectionSnyder.cs @@ -80,7 +80,7 @@ namespace MapControl return transform; } - public override Point? LocationToMap(double latitude, double longitude) + public override Point LocationToMap(double latitude, double longitude) { var phi = latitude * Math.PI / 180d; var M = MeridianDistance(phi); diff --git a/MapControl/Shared/WebMercatorProjection.cs b/MapControl/Shared/WebMercatorProjection.cs index 5c5149f7..d2f4cc03 100644 --- a/MapControl/Shared/WebMercatorProjection.cs +++ b/MapControl/Shared/WebMercatorProjection.cs @@ -34,7 +34,7 @@ namespace MapControl return new Matrix(k, 0d, 0d, k, 0d, 0d); } - public override Point? LocationToMap(double latitude, double longitude) + public override Point LocationToMap(double latitude, double longitude) { return new Point( MeterPerDegree * longitude, diff --git a/MapControl/Shared/Wgs84AutoTmProjection.cs b/MapControl/Shared/Wgs84AutoTmProjection.cs deleted file mode 100644 index b3f1c457..00000000 --- a/MapControl/Shared/Wgs84AutoTmProjection.cs +++ /dev/null @@ -1,27 +0,0 @@ -namespace MapControl -{ - /// - /// WGS84 Auto Transverse Mercator Projection - AUTO2:42002. - /// CentralMeridian is automatically set to Center.Longitude. - /// - public class Wgs84AutoTmProjection : TransverseMercatorProjection - { - public const string DefaultCrsId = "AUTO2:42002"; - - public Wgs84AutoTmProjection() // parameterless constructor for XAML - : this(DefaultCrsId) - { - } - - public Wgs84AutoTmProjection(string crsId) - { - CrsId = crsId; - EnableCenterUpdates(); - } - - protected override void CenterChanged() - { - CentralMeridian = Center.Longitude; - } - } -} diff --git a/MapControl/Shared/Wgs84AutoUtmProjection.cs b/MapControl/Shared/Wgs84AutoUtmProjection.cs deleted file mode 100644 index 9dbf8cbe..00000000 --- a/MapControl/Shared/Wgs84AutoUtmProjection.cs +++ /dev/null @@ -1,51 +0,0 @@ -using System; - -namespace MapControl -{ - /// - /// WGS84 Universal Transverse Mercator Projection - AUTO2:42002. - /// Zone and Hemisphere are automatically set according to the projection's Center. - /// If the CRS identifier passed to the constructor is null or empty, appropriate - /// values from EPSG:32601 to EPSG:32660 and EPSG:32701 to EPSG:32760 are used. - /// - public class Wgs84AutoUtmProjection : Wgs84UtmProjection - { - public const string DefaultCrsId = "AUTO2:42001"; - - private readonly string autoCrsId; - - public Wgs84AutoUtmProjection() // parameterless constructor for XAML - : this(DefaultCrsId) - { - } - - public Wgs84AutoUtmProjection(string crsId) - : base(31, Hemisphere.North) - { - autoCrsId = crsId; - - if (!string.IsNullOrEmpty(autoCrsId)) - { - CrsId = autoCrsId; - } - - EnableCenterUpdates(); - } - - protected override void CenterChanged() - { - var zone = (int)Math.Floor(Center.Longitude / 6d) + 31; - var hemisphere = Center.Latitude >= 0d ? Hemisphere.North : Hemisphere.South; - - if (Zone != zone || Hemisphere != hemisphere) - { - SetZone(zone, hemisphere); - - if (!string.IsNullOrEmpty(autoCrsId)) - { - CrsId = autoCrsId; - } - } - } - } -} diff --git a/MapControl/Shared/Wgs84UtmProjection.cs b/MapControl/Shared/Wgs84UtmProjection.cs index 12ded47c..257c98be 100644 --- a/MapControl/Shared/Wgs84UtmProjection.cs +++ b/MapControl/Shared/Wgs84UtmProjection.cs @@ -29,7 +29,7 @@ namespace MapControl SetZone(zone, hemisphere); } - protected void SetZone(int zone, Hemisphere hemisphere) + public void SetZone(int zone, Hemisphere hemisphere) { if (zone < FirstZone || zone > LastZone) { diff --git a/MapControl/Shared/WmsImageLayer.cs b/MapControl/Shared/WmsImageLayer.cs index ef5aed00..365d06d4 100644 --- a/MapControl/Shared/WmsImageLayer.cs +++ b/MapControl/Shared/WmsImageLayer.cs @@ -249,7 +249,7 @@ namespace MapControl { "LAYERS", RequestLayers ?? AvailableLayers?.FirstOrDefault() ?? "" }, { "STYLES", RequestStyles ?? "" }, { "FORMAT", "image/png" }, - { "CRS", GetCrsValue() }, + { "CRS", ParentMap.MapProjection.CrsId }, { "BBOX", GetBboxValue(bbox) }, { "WIDTH", Math.Ceiling(width).ToString("F0") }, { "HEIGHT", Math.Ceiling(height).ToString("F0") } @@ -285,7 +285,7 @@ namespace MapControl { "LAYERS", RequestLayers ?? AvailableLayers?.FirstOrDefault() ?? "" }, { "STYLES", RequestStyles ?? "" }, { "INFO_FORMAT", format }, - { "CRS", GetCrsValue() }, + { "CRS", ParentMap.MapProjection.CrsId }, { "BBOX", GetBboxValue(bbox) }, { "WIDTH", Math.Ceiling(width).ToString("F0") }, { "HEIGHT", Math.Ceiling(height).ToString("F0") }, @@ -322,28 +322,6 @@ namespace MapControl return new Uri(ServiceUri.GetLeftPart(UriPartial.Path) + "?" + query); } - protected virtual string GetCrsValue() - { - var projection = ParentMap.MapProjection; - var crs = projection.CrsId; - - if (crs.StartsWith("AUTO2:") || crs.StartsWith("AUTO:")) - { - var lon = 0d; - var lat = 0d; ; - - if (projection.Center != null) - { - lon = projection.Center.Longitude; - lat = projection.Center.Latitude; - } - - crs = string.Format(CultureInfo.InvariantCulture, "{0},1,{1:0.########},{2:0.########}", crs, lon, lat); - } - - return crs; - } - protected virtual string GetBboxValue(Rect bbox) { var crs = ParentMap.MapProjection.CrsId; diff --git a/MapControl/Shared/WorldMercatorProjection.cs b/MapControl/Shared/WorldMercatorProjection.cs index 8a131872..943944a8 100644 --- a/MapControl/Shared/WorldMercatorProjection.cs +++ b/MapControl/Shared/WorldMercatorProjection.cs @@ -36,7 +36,7 @@ namespace MapControl return new Matrix(k, 0d, 0d, k, 0d, 0d); } - public override Point? LocationToMap(double latitude, double longitude) + public override Point LocationToMap(double latitude, double longitude) { return new Point( MeterPerDegree * longitude, diff --git a/MapControl/WPF/MapPanel.WPF.cs b/MapControl/WPF/MapPanel.WPF.cs index 48a9cded..42da0b77 100644 --- a/MapControl/WPF/MapPanel.WPF.cs +++ b/MapControl/WPF/MapPanel.WPF.cs @@ -16,7 +16,7 @@ namespace MapControl FrameworkPropertyMetadataOptions.AffectsParentArrange); public static readonly DependencyProperty MapRectProperty = - DependencyPropertyHelper.RegisterAttached("MapRect", typeof(MapPanel), null, + DependencyPropertyHelper.RegisterAttached("MapRect", typeof(MapPanel), null, FrameworkPropertyMetadataOptions.AffectsParentArrange); public static MapBase GetParentMap(FrameworkElement element) diff --git a/MapControl/WPF/MapPolypoint.WPF.cs b/MapControl/WPF/MapPolypoint.WPF.cs index c5aa21a7..6956dbdd 100644 --- a/MapControl/WPF/MapPolypoint.WPF.cs +++ b/MapControl/WPF/MapPolypoint.WPF.cs @@ -36,10 +36,7 @@ namespace MapControl private void AddPolylinePoints(StreamGeometryContext context, IEnumerable locations, double longitudeOffset, bool closed) { - var points = locations - .Select(location => LocationToView(location, longitudeOffset)) - .Where(point => point.HasValue) - .Select(point => point.Value); + var points = locations.Select(location => LocationToView(location, longitudeOffset)); if (points.Any()) { diff --git a/MapControl/WinUI/MapPanel.WinUI.cs b/MapControl/WinUI/MapPanel.WinUI.cs index b6d8ecc3..0841b3de 100644 --- a/MapControl/WinUI/MapPanel.WinUI.cs +++ b/MapControl/WinUI/MapPanel.WinUI.cs @@ -22,7 +22,7 @@ namespace MapControl (element, oldValue, newValue) => (element.Parent as MapPanel)?.InvalidateArrange()); public static readonly DependencyProperty MapRectProperty = - DependencyPropertyHelper.RegisterAttached("MapRect", typeof(MapPanel), null, + DependencyPropertyHelper.RegisterAttached("MapRect", typeof(MapPanel), null, (element, oldValue, newValue) => (element.Parent as MapPanel)?.InvalidateArrange()); public static void InitMapElement(FrameworkElement element) diff --git a/MapControl/WinUI/MapPolypoint.WinUI.cs b/MapControl/WinUI/MapPolypoint.WinUI.cs index 4dce9bbc..2d52f56d 100644 --- a/MapControl/WinUI/MapPolypoint.WinUI.cs +++ b/MapControl/WinUI/MapPolypoint.WinUI.cs @@ -42,10 +42,7 @@ namespace MapControl private void AddPolylinePoints(PathFigureCollection figures, IEnumerable locations, double longitudeOffset, bool closed) { - var points = locations - .Select(location => LocationToView(location, longitudeOffset)) - .Where(point => point.HasValue) - .Select(point => point.Value); + var points = locations.Select(location => LocationToView(location, longitudeOffset)); if (points.Any()) { diff --git a/MapProjections/Shared/ProjNetMapProjection.cs b/MapProjections/Shared/ProjNetMapProjection.cs index 02c57c7b..762b2c97 100644 --- a/MapProjections/Shared/ProjNetMapProjection.cs +++ b/MapProjections/Shared/ProjNetMapProjection.cs @@ -76,22 +76,16 @@ namespace MapControl.Projections return new Matrix(1d, 0d, 0d, 1d, 0d, 0d); } - public override Point? LocationToMap(double latitude, double longitude) + public override Point LocationToMap(double latitude, double longitude) { if (LocationToMapTransform == null) { throw new InvalidOperationException("The CoordinateSystem property is not set."); } - try - { - var coordinate = LocationToMapTransform.Transform([longitude, latitude]); - return new Point(coordinate[0], coordinate[1]); - } - catch - { - return null; - } + var coordinate = LocationToMapTransform.Transform([longitude, latitude]); + + return new Point(coordinate[0], coordinate[1]); } public override Location MapToLocation(double x, double y) @@ -101,15 +95,9 @@ namespace MapControl.Projections throw new InvalidOperationException("The CoordinateSystem property is not set."); } - try - { - var coordinate = MapToLocationTransform.Transform([x, y]); - return new Location(coordinate[1], coordinate[0]); - } - catch - { - return null; - } + var coordinate = MapToLocationTransform.Transform([x, y]); + + return new Location(coordinate[1], coordinate[0]); } } } diff --git a/MapProjections/Shared/ProjNetMapProjectionFactory.cs b/MapProjections/Shared/ProjNetMapProjectionFactory.cs index 0b03ffa5..7537edc5 100644 --- a/MapProjections/Shared/ProjNetMapProjectionFactory.cs +++ b/MapProjections/Shared/ProjNetMapProjectionFactory.cs @@ -32,9 +32,6 @@ namespace MapControl.Projections MapControl.WorldMercatorProjection.DefaultCrsId => new WorldMercatorProjection(), MapControl.Wgs84UpsNorthProjection.DefaultCrsId => new Wgs84UpsNorthProjection(), MapControl.Wgs84UpsSouthProjection.DefaultCrsId => new Wgs84UpsSouthProjection(), - MapControl.Wgs84AutoUtmProjection.DefaultCrsId => new Wgs84AutoUtmProjection(), - MapControl.OrthographicProjection.DefaultCrsId => new Wgs84OrthographicProjection(), - MapControl.StereographicProjection.DefaultCrsId => new Wgs84StereographicProjection(), _ => GetProjectionFromEpsgCode(crsId) ?? base.GetProjection(crsId) }; } diff --git a/MapProjections/Shared/Wgs84AutoUtmProjection.cs b/MapProjections/Shared/Wgs84AutoUtmProjection.cs deleted file mode 100644 index 99fdaf21..00000000 --- a/MapProjections/Shared/Wgs84AutoUtmProjection.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System; - -namespace MapControl.Projections -{ - /// - /// WGS84 Universal Transverse Mercator Projection with automatic zone selection from - /// the projection center. If the CRS identifier passed to the constructor is null or empty, - /// appropriate values from EPSG:32601 to EPSG:32660 and EPSG:32701 to EPSG:32760 are used. - /// - public class Wgs84AutoUtmProjection : Wgs84UtmProjection - { - private readonly string autoCrsId; - - public Wgs84AutoUtmProjection() // parameterless constructor for XAML - : this(MapControl.Wgs84AutoUtmProjection.DefaultCrsId) - { - } - - public Wgs84AutoUtmProjection(string crsId) - : base(31, Hemisphere.North) - { - autoCrsId = crsId; - - if (!string.IsNullOrEmpty(autoCrsId)) - { - CrsId = autoCrsId; - } - } - - protected override void CenterChanged() - { - var zone = (int)Math.Floor(Center.Longitude / 6d) + 31; - var hemisphere = Center.Latitude >= 0d ? Hemisphere.North : Hemisphere.South; - - if (Zone != zone || Hemisphere != hemisphere) - { - SetZone(zone, hemisphere); - - if (!string.IsNullOrEmpty(autoCrsId)) - { - CrsId = autoCrsId; - } - } - } - } -} diff --git a/MapProjections/Shared/Wgs84OrthographicProjection.cs b/MapProjections/Shared/Wgs84OrthographicProjection.cs deleted file mode 100644 index c90627e9..00000000 --- a/MapProjections/Shared/Wgs84OrthographicProjection.cs +++ /dev/null @@ -1,44 +0,0 @@ -using System.Globalization; -#if WPF -using System.Windows.Media; -#endif - -namespace MapControl.Projections -{ - /// - /// Spherical Orthographic Projection - AUTO2:42003. - /// See "Map Projections - A Working Manual" (https://pubs.usgs.gov/publication/pp1395), p.148-150. - /// - public class Wgs84OrthographicProjection : ProjNetMapProjection - { - public Wgs84OrthographicProjection() - { - EnableCenterUpdates(); - } - - protected override void CenterChanged() - { - var wktFormat = - "PROJCS[\"WGS 84 / World Mercator\"," + - WktConstants.GeogCsWgs84 + "," + - "PROJECTION[\"Orthographic\"]," + - "PARAMETER[\"latitude_of_origin\",{0:0.########}]," + - "PARAMETER[\"central_meridian\",{1:0.########}]," + - "UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]]," + - "AXIS[\"Easting\",EAST]," + - "AXIS[\"Northing\",NORTH]" + - "AUTHORITY[\"AUTO2\",\"42003\"]]"; - - CoordinateSystemWkt = string.Format( - CultureInfo.InvariantCulture, wktFormat, Center.Latitude, Center.Longitude); - } - - public override Matrix RelativeTransform(double latitude, double longitude) - { - var p = new AzimuthalProjection.ProjectedPoint(Center.Latitude, Center.Longitude, latitude, longitude); - (var scaleX, var scaleY) = p.RelativeScale(p.CosC, 1d); // p.149 (20-5), k == 1 - - return RelativeTransform(latitude, longitude, scaleX, scaleY); - } - } -} diff --git a/MapProjections/Shared/Wgs84StereographicProjection.cs b/MapProjections/Shared/Wgs84StereographicProjection.cs deleted file mode 100644 index 8f93b01f..00000000 --- a/MapProjections/Shared/Wgs84StereographicProjection.cs +++ /dev/null @@ -1,44 +0,0 @@ -using System.Globalization; -#if WPF -using System.Windows.Media; -#endif - -namespace MapControl.Projections -{ - /// - /// Spherical Stereographic Projection - AUTO2:97002. - /// See "Map Projections - A Working Manual" (https://pubs.usgs.gov/publication/pp1395), p.157-160. - /// - public class Wgs84StereographicProjection : ProjNetMapProjection - { - public Wgs84StereographicProjection() - { - EnableCenterUpdates(); - } - - protected override void CenterChanged() - { - var wktFormat = - "PROJCS[\"WGS 84 / World Mercator\"," + - WktConstants.GeogCsWgs84 + "," + - "PROJECTION[\"Oblique_Stereographic\"]," + - "PARAMETER[\"latitude_of_origin\",{0:0.########}]," + - "PARAMETER[\"central_meridian\",{1:0.########}]," + - "UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]]," + - "AXIS[\"Easting\",EAST]," + - "AXIS[\"Northing\",NORTH]" + - "AUTHORITY[\"AUTO2\",\"97002\"]]"; - - CoordinateSystemWkt = string.Format( - CultureInfo.InvariantCulture, wktFormat, Center.Latitude, Center.Longitude); - } - - public override Matrix RelativeTransform(double latitude, double longitude) - { - var p = new AzimuthalProjection.ProjectedPoint(Center.Latitude, Center.Longitude, latitude, longitude); - var k = 2d / (1d + p.CosC); // p.157 (21-4), k0 == 1 - - return RelativeTransform(latitude, longitude, k, k); - } - } -} diff --git a/MapProjections/Shared/Wgs84UpsProjections.cs b/MapProjections/Shared/Wgs84UpsProjections.cs index 6e596ec9..055972d9 100644 --- a/MapProjections/Shared/Wgs84UpsProjections.cs +++ b/MapProjections/Shared/Wgs84UpsProjections.cs @@ -23,9 +23,7 @@ namespace MapControl.Projections public override Matrix RelativeTransform(double latitude, double longitude) { - var k = PolarStereographicProjection.RelativeScale(Hemisphere.North, Wgs84Flattening, 0.994, latitude); - - return RelativeTransform(latitude, longitude, k, k); + return PolarStereographicProjection.RelativeScale(Hemisphere.North, Wgs84Flattening, 0.994, latitude, longitude); } } @@ -48,9 +46,7 @@ namespace MapControl.Projections public override Matrix RelativeTransform(double latitude, double longitude) { - var k = PolarStereographicProjection.RelativeScale(Hemisphere.South, Wgs84Flattening, 0.994, latitude); - - return RelativeTransform(latitude, longitude, k, k); + return PolarStereographicProjection.RelativeScale(Hemisphere.North, Wgs84Flattening, 0.994, latitude, longitude); } } }