diff --git a/MapControl/Shared/AutoEquirectangularProjection.cs b/MapControl/Shared/AutoEquirectangularProjection.cs
index c4b36924..c075715d 100644
--- a/MapControl/Shared/AutoEquirectangularProjection.cs
+++ b/MapControl/Shared/AutoEquirectangularProjection.cs
@@ -1,6 +1,7 @@
using System;
#if WPF
using System.Windows;
+using System.Windows.Media;
#elif AVALONIA
using Avalonia;
#endif
@@ -27,11 +28,11 @@ namespace MapControl
CrsId = crsId;
}
- public override Point RelativeScale(double latitude, double longitude)
+ public override Matrix RelativeScale(double latitude, double longitude)
{
- return new Point(
+ return new Matrix(
Math.Cos(Center.Latitude * Math.PI / 180d) / Math.Cos(latitude * Math.PI / 180d),
- 1d);
+ 0d, 0d, 1d, 0d, 0d);
}
public override Point? LocationToMap(double latitude, double longitude)
diff --git a/MapControl/Shared/AzimuthalEquidistantProjection.cs b/MapControl/Shared/AzimuthalEquidistantProjection.cs
index 8f1fc91a..8e522514 100644
--- a/MapControl/Shared/AzimuthalEquidistantProjection.cs
+++ b/MapControl/Shared/AzimuthalEquidistantProjection.cs
@@ -1,6 +1,7 @@
using System;
#if WPF
using System.Windows;
+using System.Windows.Media;
#elif AVALONIA
using Avalonia;
#endif
@@ -25,7 +26,7 @@ namespace MapControl
CrsId = crsId;
}
- public override Point RelativeScale(double latitude, double longitude)
+ public override Matrix RelativeScale(double latitude, double longitude)
{
(var cosC, var _, var _) = GetPointValues(latitude, longitude);
var k = 1d;
@@ -36,7 +37,7 @@ namespace MapControl
k = c / Math.Sin(c); // p.195 (25-2)
}
- return new Point(k, k);
+ return new Matrix(k, 0d, 0d, k, 0d, 0d);
}
public override Point? LocationToMap(double latitude, double longitude)
diff --git a/MapControl/Shared/EquirectangularProjection.cs b/MapControl/Shared/EquirectangularProjection.cs
index a35116e9..b2832c3b 100644
--- a/MapControl/Shared/EquirectangularProjection.cs
+++ b/MapControl/Shared/EquirectangularProjection.cs
@@ -1,6 +1,7 @@
using System;
#if WPF
using System.Windows;
+using System.Windows.Media;
#elif AVALONIA
using Avalonia;
#endif
@@ -27,9 +28,9 @@ namespace MapControl
CrsId = crsId;
}
- public override Point RelativeScale(double latitude, double longitude)
+ public override Matrix RelativeScale(double latitude, double longitude)
{
- return new Point(1d / Math.Cos(latitude * Math.PI / 180d), 1d);
+ return new Matrix(1d / Math.Cos(latitude * Math.PI / 180d), 0d, 0d, 1d, 0d, 0d);
}
public override Point? LocationToMap(double latitude, double longitude)
diff --git a/MapControl/Shared/GnomonicProjection.cs b/MapControl/Shared/GnomonicProjection.cs
index 46399eb7..d633eabf 100644
--- a/MapControl/Shared/GnomonicProjection.cs
+++ b/MapControl/Shared/GnomonicProjection.cs
@@ -1,6 +1,7 @@
using System;
#if WPF
using System.Windows;
+using System.Windows.Media;
#elif AVALONIA
using Avalonia;
#endif
@@ -25,12 +26,14 @@ namespace MapControl
CrsId = crsId;
}
- public override Point RelativeScale(double latitude, double longitude)
+ public override Matrix RelativeScale(double latitude, double longitude)
{
- (var cosC, var _, var _) = GetPointValues(latitude, longitude);
- var h = 1d / (cosC * cosC); // p.165 (22-2)
+ (var cosC, var x, var y) = GetPointValues(latitude, longitude);
+ var k = 1d / cosC; // p.165 (22-3)
+ var h = k * k; // p.165 (22-2)
- return new Point(h, h); // TODO: rotate
+ var scale = new Matrix(h, 0d, 0d, h, 0d, 0d);
+ return scale;
}
public override Point? LocationToMap(double latitude, double longitude)
diff --git a/MapControl/Shared/MapBase.cs b/MapControl/Shared/MapBase.cs
index 36c790bd..58e4ef42 100644
--- a/MapControl/Shared/MapBase.cs
+++ b/MapControl/Shared/MapBase.cs
@@ -177,37 +177,25 @@ 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.
+ /// Gets a transform Matrix from meters to view coordinates for scaling and rotating
+ /// at the specified geographic coordinates.
///
- public Point GetMapScale(double latitude, double longitude)
+ public Matrix GetMapToViewTransform(double latitude, double longitude)
{
- var relativeScale = MapProjection.RelativeScale(latitude, longitude);
+ var transform = MapProjection.RelativeScale(latitude, longitude);
+ transform.Scale(ViewTransform.Scale, ViewTransform.Scale);
+ transform.Rotate(ViewTransform.Rotation);
- return new Point(ViewTransform.Scale * relativeScale.X, ViewTransform.Scale * relativeScale.Y);
- }
-
- ///
- /// Gets the map scale as horizontal and vertical scaling factors from meters to
- /// view coordinates at the specified location.
- ///
- public Point GetMapScale(Location location)
- {
- return GetMapScale(location.Latitude, location.Longitude);
+ return transform;
}
///
/// Gets a transform Matrix from meters to view coordinates for scaling and rotating
- /// objects that are anchored at the specified Location.
+ /// at the specified Location.
///
- public Matrix GetMapTransform(Location location)
+ public Matrix GetMapToViewTransform(Location location)
{
- var mapScale = GetMapScale(location.Latitude, location.Longitude);
- var transform = new Matrix(mapScale.X, 0d, 0d, mapScale.Y, 0d, 0d);
-
- transform.Rotate(ViewTransform.Rotation);
-
- return transform;
+ return GetMapToViewTransform(location.Latitude, location.Longitude);
}
///
diff --git a/MapControl/Shared/MapGraticule.cs b/MapControl/Shared/MapGraticule.cs
index 3be09c4d..86a3e990 100644
--- a/MapControl/Shared/MapGraticule.cs
+++ b/MapControl/Shared/MapGraticule.cs
@@ -75,12 +75,14 @@ namespace MapControl
set => SetValue(FontSizeProperty, value);
}
+ private double PixelPerDegree => Math.Max(1d, ParentMap.ViewTransform.Scale * MapProjection.Wgs84MeterPerDegree);
+
private double lineDistance;
private string labelFormat;
private void SetLineDistance()
{
- var minDistance = MinLineDistance / PixelPerLongitudeDegree(ParentMap.Center.Latitude, ParentMap.Center.Longitude);
+ var minDistance = MinLineDistance / PixelPerDegree;
var scale = minDistance < 1d / 60d ? 3600d : minDistance < 1d ? 60d : 1d;
minDistance *= scale;
@@ -98,14 +100,6 @@ namespace MapControl
: lineDistance < 1d ? "{0} {1}°{2:00}'" : "{0} {1}°";
}
- private double PixelPerLongitudeDegree(double latitude, double longitude)
- {
- var scale = ParentMap.GetMapScale(latitude, longitude);
-
- return Math.Max(1d, // a reasonable lower limit
- scale.X * Math.Cos(latitude * Math.PI / 180d) * MapProjection.Wgs84MeterPerDegree);
- }
-
private string GetLabelText(double value, string hemispheres)
{
var hemisphere = hemispheres[0];
@@ -131,7 +125,7 @@ namespace MapControl
{
// Get rotation from second location with same latitude.
//
- var pos = ParentMap.LocationToView(latitude, longitude + 10d / PixelPerLongitudeDegree(latitude, longitude));
+ var pos = ParentMap.LocationToView(latitude, longitude + 10d / PixelPerDegree);
if (pos.HasValue)
{
diff --git a/MapControl/Shared/MapItem.cs b/MapControl/Shared/MapItem.cs
index eff1c54b..16de22f5 100644
--- a/MapControl/Shared/MapItem.cs
+++ b/MapControl/Shared/MapItem.cs
@@ -96,7 +96,7 @@ namespace MapControl
{
if (MapTransform != null && ParentMap != null && Location != null)
{
- MapTransform.Matrix = ParentMap.GetMapTransform(Location);
+ MapTransform.Matrix = ParentMap.GetMapToViewTransform(Location);
}
}
}
diff --git a/MapControl/Shared/MapPath.cs b/MapControl/Shared/MapPath.cs
index 7e10ef8b..ed8fca86 100644
--- a/MapControl/Shared/MapPath.cs
+++ b/MapControl/Shared/MapPath.cs
@@ -66,7 +66,7 @@ namespace MapControl
{
if (ParentMap != null && Location != null && Data != null)
{
- SetDataTransform(ParentMap.GetMapTransform(Location));
+ SetDataTransform(ParentMap.GetMapToViewTransform(Location));
}
MapPanel.SetLocation(this, Location);
diff --git a/MapControl/Shared/MapProjection.cs b/MapControl/Shared/MapProjection.cs
index 288f513a..8657486f 100644
--- a/MapControl/Shared/MapProjection.cs
+++ b/MapControl/Shared/MapProjection.cs
@@ -1,6 +1,7 @@
using System;
#if WPF
using System.Windows;
+using System.Windows.Media;
#elif AVALONIA
using Avalonia;
#endif
@@ -82,9 +83,10 @@ namespace MapControl
}
///
- /// Gets the relative map scale at the specified geographic coordinates.
+ /// Gets the relative scale at the specified geographic coordinates.
+ /// The returned Matrix represents the local distortion of the map projection.
///
- public virtual Point RelativeScale(double latitude, double longitude) => new Point(1d, 1d);
+ public virtual Matrix RelativeScale(double latitude, double longitude) => new Matrix(1d, 0d, 0d, 1d, 0d, 0d);
///
/// Transforms geographic coordinates to a Point in projected map coordinates.
@@ -101,7 +103,7 @@ namespace MapControl
///
/// Gets the relative map scale at the specified geographic Location.
///
- public Point RelativeScale(Location location) => RelativeScale(location.Latitude, location.Longitude);
+ public Matrix RelativeScale(Location location) => RelativeScale(location.Latitude, location.Longitude);
///
/// Transforms a Location in geographic coordinates to a Point in projected map coordinates.
diff --git a/MapControl/Shared/MapScale.cs b/MapControl/Shared/MapScale.cs
index 6b3a7baf..572a28e3 100644
--- a/MapControl/Shared/MapScale.cs
+++ b/MapControl/Shared/MapScale.cs
@@ -88,13 +88,13 @@ namespace MapControl
protected override Size MeasureOverride(Size availableSize)
{
- double scale;
-
- if (ParentMap == null || (scale = ParentMap.GetMapScale(ParentMap.Center).X) <= 0d)
+ if (ParentMap == null)
{
return new Size();
}
+ var p = ParentMap.GetMapToViewTransform(ParentMap.Center).Transform(new Point(1d, 0d));
+ var scale = Math.Sqrt(p.X * p.X + p.Y * p.Y);
var length = MinWidth / scale;
var magnitude = Math.Pow(10d, Math.Floor(Math.Log10(length)));
diff --git a/MapControl/Shared/Matrix.cs b/MapControl/Shared/Matrix.cs
index 030a55e6..aa5b4355 100644
--- a/MapControl/Shared/Matrix.cs
+++ b/MapControl/Shared/Matrix.cs
@@ -50,6 +50,19 @@ namespace MapControl
OffsetY += y;
}
+ public void Scale(double scaleX, double scaleY)
+ {
+ // equivalent to Multiply(new Matrix(scaleX, 0, 0, scaleY, 0d, 0d));
+ //
+ SetMatrix(
+ M11 * scaleX,
+ M12 * scaleY,
+ M21 * scaleX,
+ M22 * scaleY,
+ OffsetX * scaleX,
+ OffsetY * scaleY);
+ }
+
public void Rotate(double angle)
{
if (angle != 0d)
@@ -60,12 +73,12 @@ namespace MapControl
// equivalent to Multiply(new Matrix(cos, sin, -sin, cos, 0d, 0d));
//
SetMatrix(
- cos * M11 - sin * M12,
- sin * M11 + cos * M12,
- cos * M21 - sin * M22,
- sin * M21 + cos * M22,
- cos * OffsetX - sin * OffsetY,
- sin * OffsetX + cos * OffsetY);
+ M11 * cos - M12 * sin,
+ M11 * sin + M12 * cos,
+ M21 * cos - M22 * sin,
+ M21 * sin + M22 * cos,
+ OffsetX * cos - OffsetY * sin,
+ OffsetX * sin + OffsetY * cos);
}
}
diff --git a/MapControl/Shared/OrthographicProjection.cs b/MapControl/Shared/OrthographicProjection.cs
index 10181779..db74fd8e 100644
--- a/MapControl/Shared/OrthographicProjection.cs
+++ b/MapControl/Shared/OrthographicProjection.cs
@@ -1,6 +1,7 @@
using System;
#if WPF
using System.Windows;
+using System.Windows.Media;
#elif AVALONIA
using Avalonia;
#endif
@@ -25,12 +26,13 @@ namespace MapControl
CrsId = crsId;
}
- public override Point RelativeScale(double latitude, double longitude)
+ public override Matrix RelativeScale(double latitude, double longitude)
{
(var cosC, var x, var y) = GetPointValues(latitude, longitude);
var h = cosC; // p.149 (20-5)
- return new Point(h, h);
+ var scale = new Matrix(h, 0d, 0d, h, 0d, 0d);
+ return scale;
}
public override Point? LocationToMap(double latitude, double longitude)
diff --git a/MapControl/Shared/PolarStereographicProjection.cs b/MapControl/Shared/PolarStereographicProjection.cs
index d467aa07..dd19baab 100644
--- a/MapControl/Shared/PolarStereographicProjection.cs
+++ b/MapControl/Shared/PolarStereographicProjection.cs
@@ -1,6 +1,7 @@
using System;
#if WPF
using System.Windows;
+using System.Windows.Media;
#elif AVALONIA
using Avalonia;
#endif
@@ -26,7 +27,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 double RelativeScale(Hemisphere hemisphere, double flattening, double latitude)
{
var sign = hemisphere == Hemisphere.North ? 1d : -1d;
var phi = sign * latitude * Math.PI / 180d;
@@ -37,20 +38,18 @@ namespace MapControl
var t = Math.Tan(Math.PI / 4d - phi / 2d)
/ Math.Pow((1d - eSinPhi) / (1d + eSinPhi), e / 2d); // p.161 (15-9)
- // r == ρ/a
- var r = 2d * scaleFactor * t
- / Math.Sqrt(Math.Pow(1d + e, 1d + e) * Math.Pow(1d - e, 1d - e)); // p.161 (21-33)
-
+ // r == ρ/(a*k0), omit k0 for relative scale
+ var r = 2d * 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)
return r / m; // p.161 (21-32)
}
- public override Point RelativeScale(double latitude, double longitude)
+ public override Matrix RelativeScale(double latitude, double longitude)
{
- var k = RelativeScale(Hemisphere, Flattening, ScaleFactor, latitude);
+ var k = RelativeScale(Hemisphere, Flattening, latitude);
- return new Point(k, k);
+ return new Matrix(k, 0d, 0d, k, 0d, 0d);
}
public override Point? LocationToMap(double latitude, double longitude)
diff --git a/MapControl/Shared/StereographicProjection.cs b/MapControl/Shared/StereographicProjection.cs
index 473e20f6..a1304be7 100644
--- a/MapControl/Shared/StereographicProjection.cs
+++ b/MapControl/Shared/StereographicProjection.cs
@@ -1,6 +1,7 @@
using System;
#if WPF
using System.Windows;
+using System.Windows.Media;
#elif AVALONIA
using Avalonia;
#endif
@@ -25,12 +26,12 @@ namespace MapControl
CrsId = crsId;
}
- public override Point RelativeScale(double latitude, double longitude)
+ public override Matrix RelativeScale(double latitude, double longitude)
{
(var cosC, var _, var _) = GetPointValues(latitude, longitude);
var k = 2d / (1d + cosC); // p.157 (21-4), k0 == 1
- return new Point(k, k);
+ return new Matrix(k, 0d, 0d, k, 0d, 0d);
}
public override Point? LocationToMap(double latitude, double longitude)
diff --git a/MapControl/Shared/TransverseMercatorProjection.cs b/MapControl/Shared/TransverseMercatorProjection.cs
index 2980de5e..5413474d 100644
--- a/MapControl/Shared/TransverseMercatorProjection.cs
+++ b/MapControl/Shared/TransverseMercatorProjection.cs
@@ -61,11 +61,6 @@ namespace MapControl
Flattening = Wgs84Flattening;
}
- public override Point RelativeScale(double latitude, double longitude)
- {
- return new Point(ScaleFactor, ScaleFactor); // sufficiently precise
- }
-
public override Point? LocationToMap(double latitude, double longitude)
{
#if NETFRAMEWORK
diff --git a/MapControl/Shared/WebMercatorProjection.cs b/MapControl/Shared/WebMercatorProjection.cs
index 29616819..7faa3dd5 100644
--- a/MapControl/Shared/WebMercatorProjection.cs
+++ b/MapControl/Shared/WebMercatorProjection.cs
@@ -1,6 +1,7 @@
using System;
#if WPF
using System.Windows;
+using System.Windows.Media;
#elif AVALONIA
using Avalonia;
#endif
@@ -26,11 +27,11 @@ namespace MapControl
CrsId = crsId;
}
- public override Point RelativeScale(double latitude, double longitude)
+ public override Matrix RelativeScale(double latitude, double longitude)
{
var k = 1d / Math.Cos(latitude * Math.PI / 180d); // p.44 (7-3)
- return new Point(k, k);
+ return new Matrix(k, 0d, 0d, k, 0d, 0d);
}
public override Point? LocationToMap(double latitude, double longitude)
diff --git a/MapControl/Shared/WorldMercatorProjection.cs b/MapControl/Shared/WorldMercatorProjection.cs
index b0d5cf31..b4bfee85 100644
--- a/MapControl/Shared/WorldMercatorProjection.cs
+++ b/MapControl/Shared/WorldMercatorProjection.cs
@@ -1,6 +1,7 @@
using System;
#if WPF
using System.Windows;
+using System.Windows.Media;
#elif AVALONIA
using Avalonia;
#endif
@@ -28,11 +29,11 @@ namespace MapControl
CrsId = crsId;
}
- public override Point RelativeScale(double latitude, double longitude)
+ public override Matrix RelativeScale(double latitude, double longitude)
{
var k = RelativeScale(latitude);
- return new Point(k, k);
+ return new Matrix(k, 0d, 0d, k, 0d, 0d);
}
public override Point? LocationToMap(double latitude, double longitude)
diff --git a/MapProjections/Shared/ProjNetMapProjection.cs b/MapProjections/Shared/ProjNetMapProjection.cs
index 0246a7f3..39ecab94 100644
--- a/MapProjections/Shared/ProjNetMapProjection.cs
+++ b/MapProjections/Shared/ProjNetMapProjection.cs
@@ -93,13 +93,6 @@ namespace MapControl.Projections
public MathTransform MapToLocationTransform { get; private set; }
- public override Point RelativeScale(double latitude, double longitude)
- {
- var k = CoordinateSystem?.Projection?.GetParameter("scale_factor")?.Value ?? 1d;
-
- return new Point(k, k);
- }
-
public override Point? LocationToMap(double latitude, double longitude)
{
if (LocationToMapTransform == null)
diff --git a/MapProjections/Shared/WebMercatorProjection.cs b/MapProjections/Shared/WebMercatorProjection.cs
index 923c53d4..36aa9a68 100644
--- a/MapProjections/Shared/WebMercatorProjection.cs
+++ b/MapProjections/Shared/WebMercatorProjection.cs
@@ -1,9 +1,7 @@
using ProjNet.CoordinateSystems;
using System;
#if WPF
-using System.Windows;
-#elif AVALONIA
-using Avalonia;
+using System.Windows.Media;
#endif
namespace MapControl.Projections
@@ -19,11 +17,11 @@ namespace MapControl.Projections
CoordinateSystem = ProjectedCoordinateSystem.WebMercator;
}
- public override Point RelativeScale(double latitude, double longitude)
+ public override Matrix RelativeScale(double latitude, double longitude)
{
var k = 1d / Math.Cos(latitude * Math.PI / 180d); // p.44 (7-3)
- return new Point(k, k);
+ return new Matrix(k, 0d, 0d, k, 0d, 0d);
}
}
}
diff --git a/MapProjections/Shared/Wgs84UpsProjections.cs b/MapProjections/Shared/Wgs84UpsProjections.cs
index a1628e6d..36d14f0b 100644
--- a/MapProjections/Shared/Wgs84UpsProjections.cs
+++ b/MapProjections/Shared/Wgs84UpsProjections.cs
@@ -1,7 +1,5 @@
#if WPF
-using System.Windows;
-#elif AVALONIA
-using Avalonia;
+using System.Windows.Media;
#endif
namespace MapControl.Projections
@@ -23,11 +21,11 @@ namespace MapControl.Projections
"AUTHORITY[\"EPSG\",\"32661\"]]";
}
- public override Point RelativeScale(double latitude, double longitude)
+ public override Matrix RelativeScale(double latitude, double longitude)
{
- var k = PolarStereographicProjection.RelativeScale(Hemisphere.North, Wgs84Flattening, 0.994, latitude);
+ var k = PolarStereographicProjection.RelativeScale(Hemisphere.North, Wgs84Flattening, latitude);
- return new Point(k, k);
+ return new Matrix(k, 0d, 0d, k, 0d, 0d);
}
}
@@ -48,11 +46,11 @@ namespace MapControl.Projections
"AUTHORITY[\"EPSG\",\"32761\"]]";
}
- public override Point RelativeScale(double latitude, double longitude)
+ public override Matrix RelativeScale(double latitude, double longitude)
{
- var k = PolarStereographicProjection.RelativeScale(Hemisphere.South, Wgs84Flattening, 0.994, latitude);
+ var k = PolarStereographicProjection.RelativeScale(Hemisphere.South, Wgs84Flattening, latitude);
- return new Point(k, k);
+ return new Matrix(k, 0d, 0d, k, 0d, 0d);
}
}
}
diff --git a/MapProjections/Shared/WorldMercatorProjection.cs b/MapProjections/Shared/WorldMercatorProjection.cs
index 92ebb6f0..8a6b6352 100644
--- a/MapProjections/Shared/WorldMercatorProjection.cs
+++ b/MapProjections/Shared/WorldMercatorProjection.cs
@@ -1,7 +1,5 @@
#if WPF
-using System.Windows;
-#elif AVALONIA
-using Avalonia;
+using System.Windows.Media;
#endif
namespace MapControl.Projections
@@ -29,11 +27,11 @@ namespace MapControl.Projections
"AUTHORITY[\"EPSG\",\"3395\"]]";
}
- public override Point RelativeScale(double latitude, double longitude)
+ public override Matrix RelativeScale(double latitude, double longitude)
{
var k = MapControl.WorldMercatorProjection.RelativeScale(latitude);
- return new Point(k, k);
+ return new Matrix(k, 0d, 0d, k, 0d, 0d);
}
}
}