diff --git a/MapControl/Avalonia/MapPolypoint.Avalonia.cs b/MapControl/Avalonia/MapPolypoint.Avalonia.cs index 1776902e..d2e40abb 100644 --- a/MapControl/Avalonia/MapPolypoint.Avalonia.cs +++ b/MapControl/Avalonia/MapPolypoint.Avalonia.cs @@ -81,10 +81,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 = LocationsToView(locations, longitudeOffset); if (points.Any()) { diff --git a/MapControl/Shared/AutoEquirectangularProjection.cs b/MapControl/Shared/AutoEquirectangularProjection.cs index 0447749d..c9c441e9 100644 --- a/MapControl/Shared/AutoEquirectangularProjection.cs +++ b/MapControl/Shared/AutoEquirectangularProjection.cs @@ -28,25 +28,25 @@ namespace MapControl CrsId = crsId; } - public override Point GetRelativeScale(Location location) + public override Point GetRelativeScale(double latitude, double longitude) { return new Point( - Math.Cos(Center.Latitude * Math.PI / 180d) / Math.Cos(location.Latitude * Math.PI / 180d), + Math.Cos(Center.Latitude * Math.PI / 180d) / Math.Cos(latitude * Math.PI / 180d), 1d); } - public override Point? LocationToMap(Location location) + public override Point? LocationToMap(double latitude, double longitude) { return new Point( - Wgs84MeterPerDegree * (location.Longitude - Center.Longitude) * Math.Cos(Center.Latitude * Math.PI / 180d), - Wgs84MeterPerDegree * location.Latitude); + Wgs84MeterPerDegree * (longitude - Center.Longitude) * Math.Cos(Center.Latitude * Math.PI / 180d), + Wgs84MeterPerDegree * latitude); } - public override Location MapToLocation(Point point) + public override Location MapToLocation(double x, double y) { return new Location( - point.Y / Wgs84MeterPerDegree, - point.X / (Wgs84MeterPerDegree * Math.Cos(Center.Latitude * Math.PI / 180d)) + Center.Longitude); + y / Wgs84MeterPerDegree, + x / (Wgs84MeterPerDegree * Math.Cos(Center.Latitude * Math.PI / 180d)) + Center.Longitude); } } } diff --git a/MapControl/Shared/AzimuthalEquidistantProjection.cs b/MapControl/Shared/AzimuthalEquidistantProjection.cs index fe31e90c..5a1eb738 100644 --- a/MapControl/Shared/AzimuthalEquidistantProjection.cs +++ b/MapControl/Shared/AzimuthalEquidistantProjection.cs @@ -26,30 +26,30 @@ namespace MapControl CrsId = crsId; } - public override Point? LocationToMap(Location location) + public override Point? LocationToMap(double latitude, double longitude) { - if (location.Equals(Center)) + if (Location.Equals(latitude, Center.Latitude) && + Location.Equals(longitude, Center.Longitude)) { return new Point(); } - Center.GetAzimuthDistance(location, out double azimuth, out double distance); + Center.GetAzimuthDistance(latitude, longitude, out double azimuth, out double distance); var mapDistance = distance * Wgs84EquatorialRadius; return new Point(mapDistance * Math.Sin(azimuth), mapDistance * Math.Cos(azimuth)); } - public override Location MapToLocation(Point point) + public override Location MapToLocation(double x, double y) { - if (point.X == 0d && point.Y == 0d) + if (x == 0d && y == 0d) { return new Location(Center.Latitude, Center.Longitude); } - var azimuth = Math.Atan2(point.X, point.Y); - var mapDistance = Math.Sqrt(point.X * point.X + point.Y * point.Y); - + var azimuth = Math.Atan2(x, y); + var mapDistance = Math.Sqrt(x * x + y * y); var distance = mapDistance / Wgs84EquatorialRadius; return Center.GetLocation(azimuth, distance); diff --git a/MapControl/Shared/AzimuthalProjection.cs b/MapControl/Shared/AzimuthalProjection.cs index 48dad081..d7aca5e4 100644 --- a/MapControl/Shared/AzimuthalProjection.cs +++ b/MapControl/Shared/AzimuthalProjection.cs @@ -37,8 +37,7 @@ namespace MapControl public override BoundingBox MapToBoundingBox(Rect rect) { BoundingBox boundingBox = null; - var rectCenter = new Point(rect.X + rect.Width / 2d, rect.Y + rect.Height / 2d); - var center = MapToLocation(rectCenter); + var center = MapToLocation(rect.X + rect.Width / 2d, rect.Y + rect.Height / 2d); if (center != null) { diff --git a/MapControl/Shared/EquirectangularProjection.cs b/MapControl/Shared/EquirectangularProjection.cs index 10178dad..b5c8c746 100644 --- a/MapControl/Shared/EquirectangularProjection.cs +++ b/MapControl/Shared/EquirectangularProjection.cs @@ -28,25 +28,19 @@ namespace MapControl CrsId = crsId; } - public override Point GetRelativeScale(Location location) + public override Point GetRelativeScale(double latitude, double longitude) { - return new Point( - 1d / Math.Cos(location.Latitude * Math.PI / 180d), - 1d); + return new Point(1d / Math.Cos(latitude * Math.PI / 180d), 1d); } - public override Point? LocationToMap(Location location) + public override Point? LocationToMap(double latitude, double longitude) { - return new Point( - Wgs84MeterPerDegree * location.Longitude, - Wgs84MeterPerDegree * location.Latitude); + return new Point(Wgs84MeterPerDegree * longitude, Wgs84MeterPerDegree * latitude); } - public override Location MapToLocation(Point point) + public override Location MapToLocation(double x, double y) { - return new Location( - point.Y / Wgs84MeterPerDegree, - point.X / Wgs84MeterPerDegree); + return new Location(y / Wgs84MeterPerDegree, x / Wgs84MeterPerDegree); } } } diff --git a/MapControl/Shared/GnomonicProjection.cs b/MapControl/Shared/GnomonicProjection.cs index f50dfca2..44699cbc 100644 --- a/MapControl/Shared/GnomonicProjection.cs +++ b/MapControl/Shared/GnomonicProjection.cs @@ -26,14 +26,15 @@ namespace MapControl CrsId = crsId; } - public override Point? LocationToMap(Location location) + public override Point? LocationToMap(double latitude, double longitude) { - if (location.Equals(Center)) + if (Location.Equals(latitude, Center.Latitude) && + Location.Equals(longitude, Center.Longitude)) { return new Point(); } - Center.GetAzimuthDistance(location, out double azimuth, out double distance); + Center.GetAzimuthDistance(latitude, longitude, out double azimuth, out double distance); var mapDistance = distance < Math.PI / 2d ? Math.Tan(distance) * Wgs84EquatorialRadius @@ -42,16 +43,15 @@ namespace MapControl return new Point(mapDistance * Math.Sin(azimuth), mapDistance * Math.Cos(azimuth)); } - public override Location MapToLocation(Point point) + public override Location MapToLocation(double x, double y) { - if (point.X == 0d && point.Y == 0d) + if (x == 0d && y == 0d) { return new Location(Center.Latitude, Center.Longitude); } - var azimuth = Math.Atan2(point.X, point.Y); - var mapDistance = Math.Sqrt(point.X * point.X + point.Y * point.Y); - + var azimuth = Math.Atan2(x, y); + var mapDistance = Math.Sqrt(x * x + y * y); var distance = Math.Atan(mapDistance / Wgs84EquatorialRadius); return Center.GetLocation(azimuth, distance); diff --git a/MapControl/Shared/Location.cs b/MapControl/Shared/Location.cs index aa981096..143db6ab 100644 --- a/MapControl/Shared/Location.cs +++ b/MapControl/Shared/Location.cs @@ -33,8 +33,8 @@ namespace MapControl public bool Equals(Location location) { return location != null && - Math.Abs(location.Latitude - Latitude) < 1e-9 && - Math.Abs(location.Longitude - Longitude) < 1e-9; + Equals(Latitude, location.Latitude) && + Equals(Longitude, location.Longitude); } public override bool Equals(object obj) @@ -52,6 +52,11 @@ namespace MapControl return string.Format(CultureInfo.InvariantCulture, "{0},{1}", Latitude, Longitude); } + public static bool CoordinateEquals(double coordinate1, double coordinate2) + { + return Math.Abs(coordinate1 - coordinate2) < 1e-9; + } + /// /// Creates a Location instance from a string containing a comma-separated pair of floating point numbers. /// @@ -87,12 +92,12 @@ namespace MapControl /// /// Calculates great circle azimuth and distance in radians between this and the specified Location. /// - public void GetAzimuthDistance(Location location, out double azimuth, out double distance) + public void GetAzimuthDistance(double latitude, double longitude, out double azimuth, out double distance) { var lat1 = Latitude * Math.PI / 180d; var lon1 = Longitude * Math.PI / 180d; - var lat2 = location.Latitude * Math.PI / 180d; - var lon2 = location.Longitude * Math.PI / 180d; + var lat2 = latitude * Math.PI / 180d; + var lon2 = longitude * Math.PI / 180d; var cosLat1 = Math.Cos(lat1); var sinLat1 = Math.Sin(lat1); var cosLat2 = Math.Cos(lat2); @@ -112,7 +117,7 @@ namespace MapControl /// public double GetDistance(Location location, double earthRadius = MapProjection.Wgs84EquatorialRadius) { - GetAzimuthDistance(location, out double _, out double distance); + GetAzimuthDistance(location.Latitude, location.Longitude, out _, out double distance); return earthRadius * distance; } diff --git a/MapControl/Shared/MapBase.cs b/MapControl/Shared/MapBase.cs index 18925dbc..2b3a2328 100644 --- a/MapControl/Shared/MapBase.cs +++ b/MapControl/Shared/MapBase.cs @@ -176,6 +176,15 @@ namespace MapControl /// public ViewTransform ViewTransform { get; } = new ViewTransform(); + /// + /// Gets the map scale as horizontal and vertical scaling factors from meters to + /// view coordinates at the specified geographic coordinates. + /// + public Point GetScale(double latitude, double longitude) + { + return ViewTransform.GetMapScale(MapProjection.GetRelativeScale(latitude, longitude)); + } + /// /// Gets the map scale as horizontal and vertical scaling factors from meters to /// view coordinates at the specified location. @@ -187,7 +196,7 @@ namespace MapControl /// /// Gets a transform Matrix from meters to view coordinates for scaling and rotating - /// objects that are anchored at a Location. + /// objects that are anchored at the specified Location. /// public Matrix GetMapTransform(Location location) { @@ -195,11 +204,11 @@ namespace MapControl } /// - /// Transforms a Location in geographic coordinates to a Point in view coordinates. + /// Transforms geographic coordinates to a Point in view coordinates. /// - public Point? LocationToView(Location location) + public Point? LocationToView(double latitude, double longitude) { - var point = MapProjection.LocationToMap(location); + var point = MapProjection.LocationToMap(latitude, longitude); if (point.HasValue) { @@ -209,6 +218,14 @@ namespace MapControl return point; } + /// + /// Transforms a Location in geographic coordinates to a Point in view coordinates. + /// + public Point? LocationToView(Location location) + { + return LocationToView(location.Latitude, location.Longitude); + } + /// /// Transforms a Point in view coordinates to a Location in geographic coordinates. /// @@ -343,8 +360,9 @@ namespace MapControl if (mapRect.HasValue) { - var rectCenter = new Point(mapRect.Value.X + mapRect.Value.Width / 2d, mapRect.Value.Y + mapRect.Value.Height / 2d); - var targetCenter = MapProjection.MapToLocation(rectCenter); + var targetCenter = MapProjection.MapToLocation( + mapRect.Value.X + mapRect.Value.Width / 2d, + mapRect.Value.Y + mapRect.Value.Height / 2d); if (targetCenter != null) { @@ -428,7 +446,7 @@ namespace MapControl if (projection.Type <= MapProjectionType.NormalCylindrical) { - var maxLocation = projection.MapToLocation(new Point(0d, 180d * MapProjection.Wgs84MeterPerDegree)); + var maxLocation = projection.MapToLocation(0d, 180d * MapProjection.Wgs84MeterPerDegree); if (maxLocation != null && maxLocation.Latitude < 90d) { diff --git a/MapControl/Shared/MapGraticule.cs b/MapControl/Shared/MapGraticule.cs index 378a83f1..3544d6e5 100644 --- a/MapControl/Shared/MapGraticule.cs +++ b/MapControl/Shared/MapGraticule.cs @@ -80,7 +80,7 @@ namespace MapControl private void SetLineDistance() { - var minDistance = MinLineDistance / PixelPerLongitudeDegree(ParentMap.Center); + var minDistance = MinLineDistance / PixelPerLongitudeDegree(ParentMap.Center.Latitude, ParentMap.Center.Longitude); var scale = minDistance < 1d / 60d ? 3600d : minDistance < 1d ? 60d : 1d; minDistance *= scale; @@ -98,11 +98,12 @@ namespace MapControl : lineDistance < 1d ? "{0} {1}°{2:00}'" : "{0} {1}°"; } - private double PixelPerLongitudeDegree(Location location) + private double PixelPerLongitudeDegree(double latitude, double longitude) { + var scale = ParentMap.GetScale(latitude, longitude); + return Math.Max(1d, // a reasonable lower limit - ParentMap.GetScale(location).X * - Math.Cos(location.Latitude * Math.PI / 180d) * MapProjection.Wgs84MeterPerDegree); + scale.X * Math.Cos(latitude * Math.PI / 180d) * MapProjection.Wgs84MeterPerDegree); } private string GetLabelText(double value, string hemispheres) @@ -121,7 +122,7 @@ namespace MapControl labelFormat, hemisphere, seconds / 3600, seconds / 60 % 60, seconds % 60); } - private void AddLabel(List