diff --git a/FileDbCache/UWP/Properties/AssemblyInfo.cs b/FileDbCache/UWP/Properties/AssemblyInfo.cs
index fa00c14f..0304a72b 100644
--- a/FileDbCache/UWP/Properties/AssemblyInfo.cs
+++ b/FileDbCache/UWP/Properties/AssemblyInfo.cs
@@ -7,8 +7,8 @@ using System.Runtime.InteropServices;
[assembly: AssemblyCompany("Clemens Fischer")]
[assembly: AssemblyCopyright("Copyright © 2020 Clemens Fischer")]
[assembly: AssemblyTrademark("")]
-[assembly: AssemblyVersion("4.17.0")]
-[assembly: AssemblyFileVersion("4.17.0")]
+[assembly: AssemblyVersion("5.0.0")]
+[assembly: AssemblyFileVersion("5.0.0")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCulture("")]
[assembly: ComVisible(false)]
diff --git a/FileDbCache/WPF/Properties/AssemblyInfo.cs b/FileDbCache/WPF/Properties/AssemblyInfo.cs
index a2e41b5d..384d334e 100644
--- a/FileDbCache/WPF/Properties/AssemblyInfo.cs
+++ b/FileDbCache/WPF/Properties/AssemblyInfo.cs
@@ -7,8 +7,8 @@ using System.Runtime.InteropServices;
[assembly: AssemblyCompany("Clemens Fischer")]
[assembly: AssemblyCopyright("Copyright © 2020 Clemens Fischer")]
[assembly: AssemblyTrademark("")]
-[assembly: AssemblyVersion("4.17.0")]
-[assembly: AssemblyFileVersion("4.17.0")]
+[assembly: AssemblyVersion("5.0.0")]
+[assembly: AssemblyFileVersion("5.0.0")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCulture("")]
[assembly: ComVisible(false)]
diff --git a/MBTiles/UWP/Properties/AssemblyInfo.cs b/MBTiles/UWP/Properties/AssemblyInfo.cs
index 163ccd7c..caf20f8c 100644
--- a/MBTiles/UWP/Properties/AssemblyInfo.cs
+++ b/MBTiles/UWP/Properties/AssemblyInfo.cs
@@ -7,8 +7,8 @@ using System.Runtime.InteropServices;
[assembly: AssemblyCompany("Clemens Fischer")]
[assembly: AssemblyCopyright("Copyright © 2020 Clemens Fischer")]
[assembly: AssemblyTrademark("")]
-[assembly: AssemblyVersion("4.17.0")]
-[assembly: AssemblyFileVersion("4.17.0")]
+[assembly: AssemblyVersion("5.0.0")]
+[assembly: AssemblyFileVersion("5.0.0")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCulture("")]
[assembly: ComVisible(false)]
diff --git a/MBTiles/WPF/Properties/AssemblyInfo.cs b/MBTiles/WPF/Properties/AssemblyInfo.cs
index 01f28b26..26ac782f 100644
--- a/MBTiles/WPF/Properties/AssemblyInfo.cs
+++ b/MBTiles/WPF/Properties/AssemblyInfo.cs
@@ -7,8 +7,8 @@ using System.Runtime.InteropServices;
[assembly: AssemblyCompany("Clemens Fischer")]
[assembly: AssemblyCopyright("Copyright © 2020 Clemens Fischer")]
[assembly: AssemblyTrademark("")]
-[assembly: AssemblyVersion("4.17.0")]
-[assembly: AssemblyFileVersion("4.17.0")]
+[assembly: AssemblyVersion("5.0.0")]
+[assembly: AssemblyFileVersion("5.0.0")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCulture("")]
[assembly: ComVisible(false)]
diff --git a/MapControl/Shared/AutoEquirectangularProjection.cs b/MapControl/Shared/AutoEquirectangularProjection.cs
index a333446c..6949e530 100644
--- a/MapControl/Shared/AutoEquirectangularProjection.cs
+++ b/MapControl/Shared/AutoEquirectangularProjection.cs
@@ -16,22 +16,22 @@ namespace MapControl
CrsId = "AUTO2:42004";
}
- public override Point LocationToPoint(Location location)
+ public override Point LocationToMap(Location location)
{
- var xScale = Wgs84MetersPerDegree * Math.Cos(ProjectionCenter.Latitude * Math.PI / 180d);
+ var xScale = UnitsPerDegree * Math.Cos(Center.Latitude * Math.PI / 180d);
return new Point(
- xScale * (location.Longitude - ProjectionCenter.Longitude),
- Wgs84MetersPerDegree * location.Latitude);
+ xScale * (location.Longitude - Center.Longitude),
+ UnitsPerDegree * location.Latitude);
}
- public override Location PointToLocation(Point point)
+ public override Location MapToLocation(Point point)
{
- var xScale = Wgs84MetersPerDegree * Math.Cos(ProjectionCenter.Latitude * Math.PI / 180d);
+ var xScale = UnitsPerDegree * Math.Cos(Center.Latitude * Math.PI / 180d);
return new Location(
- point.Y / Wgs84MetersPerDegree,
- point.X / xScale + ProjectionCenter.Longitude);
+ point.Y / UnitsPerDegree,
+ point.X / xScale + Center.Longitude);
}
}
}
diff --git a/MapControl/Shared/AzimuthalEquidistantProjection.cs b/MapControl/Shared/AzimuthalEquidistantProjection.cs
index 4ba2ea68..fd77c207 100644
--- a/MapControl/Shared/AzimuthalEquidistantProjection.cs
+++ b/MapControl/Shared/AzimuthalEquidistantProjection.cs
@@ -16,35 +16,35 @@ namespace MapControl
{
// No standard CRS ID
- public override Point LocationToPoint(Location location)
+ public override Point LocationToMap(Location location)
{
- if (location.Equals(ProjectionCenter))
+ if (location.Equals(Center))
{
return new Point();
}
double azimuth, distance;
- GetAzimuthDistance(ProjectionCenter, location, out azimuth, out distance);
+ GetAzimuthDistance(Center, location, out azimuth, out distance);
- var mapDistance = distance * TrueScale * 180d / Math.PI;
+ var mapDistance = distance * UnitsPerDegree * 180d / Math.PI;
return new Point(mapDistance * Math.Sin(azimuth), mapDistance * Math.Cos(azimuth));
}
- public override Location PointToLocation(Point point)
+ public override Location MapToLocation(Point point)
{
if (point.X == 0d && point.Y == 0d)
{
- return new Location(ProjectionCenter.Latitude, ProjectionCenter.Longitude);
+ 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 distance = mapDistance / (TrueScale * 180d / Math.PI);
+ var distance = mapDistance / (UnitsPerDegree * 180d / Math.PI);
- return GetLocation(ProjectionCenter, azimuth, distance);
+ return GetLocation(Center, azimuth, distance);
}
}
}
diff --git a/MapControl/Shared/AzimuthalProjection.cs b/MapControl/Shared/AzimuthalProjection.cs
index 021d4bd4..1b77cfe2 100644
--- a/MapControl/Shared/AzimuthalProjection.cs
+++ b/MapControl/Shared/AzimuthalProjection.cs
@@ -27,7 +27,7 @@ namespace MapControl
if (cbbox != null)
{
- var center = LocationToPoint(cbbox.Center);
+ var center = LocationToMap(cbbox.Center);
return new Rect(
center.X - cbbox.Width / 2d, center.Y - cbbox.Height / 2d,
@@ -39,7 +39,7 @@ namespace MapControl
public override BoundingBox RectToBoundingBox(Rect rect)
{
- var center = PointToLocation(new Point(rect.X + rect.Width / 2d, rect.Y + rect.Height / 2d));
+ var center = MapToLocation(new Point(rect.X + rect.Width / 2d, rect.Y + rect.Height / 2d));
return new CenteredBoundingBox(center, rect.Width, rect.Height); // width and height in meters
}
diff --git a/MapControl/Shared/EquirectangularProjection.cs b/MapControl/Shared/EquirectangularProjection.cs
index 192a24a6..ebdb3c0b 100644
--- a/MapControl/Shared/EquirectangularProjection.cs
+++ b/MapControl/Shared/EquirectangularProjection.cs
@@ -14,7 +14,7 @@ namespace MapControl
{
///
/// Equirectangular Projection.
- /// Longitude and Latitude values are transformed identically to X and Y.
+ /// Longitude and Latitude values are transformed linearly to X and Y in meters.
///
public class EquirectangularProjection : MapProjection
{
@@ -23,33 +23,33 @@ namespace MapControl
CrsId = "EPSG:4326";
}
- public override double TrueScale
- {
- get { return 1d; }
- }
-
- public override Vector GetMapScale(Location location)
+ public override Vector GetRelativeScale(Location location)
{
return new Vector(
- ViewportScale / (Wgs84MetersPerDegree * Math.Cos(location.Latitude * Math.PI / 180d)),
- ViewportScale / Wgs84MetersPerDegree);
+ 1d / Math.Cos(location.Latitude * Math.PI / 180d),
+ 1d);
}
- public override Point LocationToPoint(Location location)
+ public override Point LocationToMap(Location location)
{
- return new Point(location.Longitude, location.Latitude);
+ return new Point(
+ location.Longitude * UnitsPerDegree,
+ location.Latitude * UnitsPerDegree);
}
- public override Location PointToLocation(Point point)
+ public override Location MapToLocation(Point point)
{
- return new Location(point.Y, point.X);
+ return new Location(
+ point.Y / UnitsPerDegree,
+ point.X / UnitsPerDegree);
}
public override string GetBboxValue(Rect rect)
{
return string.Format(CultureInfo.InvariantCulture,
CrsId != "CRS:84" ? "{1},{0},{3},{2}" : "{0},{1},{2},{3}",
- rect.X, rect.Y, (rect.X + rect.Width), (rect.Y + rect.Height));
+ rect.X / UnitsPerDegree, rect.Y / UnitsPerDegree,
+ (rect.X + rect.Width) / UnitsPerDegree, (rect.Y + rect.Height) / UnitsPerDegree);
}
}
}
diff --git a/MapControl/Shared/GnomonicProjection.cs b/MapControl/Shared/GnomonicProjection.cs
index 52361d5a..be7976cb 100644
--- a/MapControl/Shared/GnomonicProjection.cs
+++ b/MapControl/Shared/GnomonicProjection.cs
@@ -19,37 +19,37 @@ namespace MapControl
CrsId = "AUTO2:97001"; // GeoServer non-standard CRS ID
}
- public override Point LocationToPoint(Location location)
+ public override Point LocationToMap(Location location)
{
- if (location.Equals(ProjectionCenter))
+ if (location.Equals(Center))
{
return new Point();
}
double azimuth, distance;
- GetAzimuthDistance(ProjectionCenter, location, out azimuth, out distance);
+ GetAzimuthDistance(Center, location, out azimuth, out distance);
var mapDistance = distance < Math.PI / 2d
- ? Math.Tan(distance) * TrueScale * 180d / Math.PI
+ ? Math.Tan(distance) * UnitsPerDegree * 180d / Math.PI
: double.PositiveInfinity;
return new Point(mapDistance * Math.Sin(azimuth), mapDistance * Math.Cos(azimuth));
}
- public override Location PointToLocation(Point point)
+ public override Location MapToLocation(Point point)
{
if (point.X == 0d && point.Y == 0d)
{
- return new Location(ProjectionCenter.Latitude, ProjectionCenter.Longitude);
+ 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 distance = Math.Atan(mapDistance / (TrueScale * 180d / Math.PI));
+ var distance = Math.Atan(mapDistance / (UnitsPerDegree * 180d / Math.PI));
- return GetLocation(ProjectionCenter, azimuth, distance);
+ return GetLocation(Center, azimuth, distance);
}
}
}
diff --git a/MapControl/Shared/MapBase.cs b/MapControl/Shared/MapBase.cs
index 5e664769..ce4d785f 100644
--- a/MapControl/Shared/MapBase.cs
+++ b/MapControl/Shared/MapBase.cs
@@ -4,6 +4,7 @@
using System;
#if WINDOWS_UWP
+using Windows.Foundation;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Media.Animation;
@@ -22,7 +23,8 @@ namespace MapControl
}
///
- /// The map control. Displays map content provided by one or more MapTileLayers or MapImageLayers.
+ /// The map control. Displays map content provided by one or more tile or image layers,
+ /// i.e. MapTileLayerBase or MapImageLayer instances.
/// The visible map area is defined by the Center and ZoomLevel properties.
/// The map can be rotated by an angle that is given by the Heading property.
/// MapBase can contain map overlay child elements like other MapPanels or MapItemsControls.
@@ -37,7 +39,7 @@ namespace MapControl
public static readonly DependencyProperty MapProjectionProperty = DependencyProperty.Register(
nameof(MapProjection), typeof(MapProjection), typeof(MapBase),
- new PropertyMetadata(null, (o, e) => ((MapBase)o).MapProjectionPropertyChanged()));
+ new PropertyMetadata(new WebMercatorProjection(), (o, e) => ((MapBase)o).MapProjectionPropertyChanged()));
public static readonly DependencyProperty ProjectionCenterProperty = DependencyProperty.Register(
nameof(ProjectionCenter), typeof(Location), typeof(MapBase),
@@ -224,6 +226,11 @@ namespace MapControl
///
/// Gets the transformation from cartesian map coordinates to viewport coordinates.
///
+ public ViewTransform ViewTransform { get; } = new ViewTransform();
+
+ ///
+ /// Gets the transformation from cartesian map coordinates to viewport coordinates as MatrixTransform.
+ ///
public MatrixTransform ViewportTransform { get; } = new MatrixTransform();
///
@@ -239,14 +246,23 @@ namespace MapControl
///
/// Gets the combination of ScaleTransform and RotateTransform
///
- public TransformGroup ScaleRotateTransform { get; } = new TransformGroup();
+ public TransformGroup ScaleRotateTransform
+ {
+ get
+ {
+ var transform = new TransformGroup();
+ transform.Children.Add(ScaleTransform);
+ transform.Children.Add(RotateTransform);
+ return transform;
+ }
+ }
///
/// Transforms a Location in geographic coordinates to a Point in viewport coordinates.
///
public Point LocationToViewportPoint(Location location)
{
- return MapProjection.LocationToViewportPoint(location);
+ return ViewTransform.MapToView(MapProjection.LocationToMap(location));
}
///
@@ -254,7 +270,25 @@ namespace MapControl
///
public Location ViewportPointToLocation(Point point)
{
- return MapProjection.ViewportPointToLocation(point);
+ return MapProjection.MapToLocation(ViewTransform.ViewToMap(point));
+ }
+
+ ///
+ /// Transforms a Rect in viewport coordinates to a BoundingBox in geographic coordinates.
+ ///
+ public BoundingBox ViewportRectToBoundingBox(Rect rect)
+ {
+ var p1 = ViewTransform.ViewToMap(new Point(rect.X, rect.Y));
+ var p2 = ViewTransform.ViewToMap(new Point(rect.X, rect.Y + rect.Height));
+ var p3 = ViewTransform.ViewToMap(new Point(rect.X + rect.Width, rect.Y));
+ var p4 = ViewTransform.ViewToMap(new Point(rect.X + rect.Width, rect.Y + rect.Height));
+
+ rect.X = Math.Min(p1.X, Math.Min(p2.X, Math.Min(p3.X, p4.X)));
+ rect.Y = Math.Min(p1.Y, Math.Min(p2.Y, Math.Min(p3.Y, p4.Y)));
+ rect.Width = Math.Max(p1.X, Math.Max(p2.X, Math.Max(p3.X, p4.X))) - rect.X;
+ rect.Height = Math.Max(p1.Y, Math.Max(p2.Y, Math.Max(p3.Y, p4.Y))) - rect.Y;
+
+ return MapProjection.RectToBoundingBox(rect);
}
///
@@ -263,7 +297,7 @@ namespace MapControl
///
public void SetTransformCenter(Point center)
{
- transformCenter = MapProjection.ViewportPointToLocation(center);
+ transformCenter = ViewportPointToLocation(center);
viewportCenter = center;
}
@@ -289,7 +323,7 @@ namespace MapControl
if (translation.X != 0d || translation.Y != 0d)
{
- Center = MapProjection.ViewportPointToLocation(viewportCenter - translation);
+ Center = ViewportPointToLocation(viewportCenter - translation);
}
}
@@ -302,7 +336,7 @@ namespace MapControl
{
if (rotation != 0d || scale != 1d)
{
- transformCenter = MapProjection.ViewportPointToLocation(center);
+ transformCenter = ViewportPointToLocation(center);
viewportCenter = center + translation;
if (rotation != 0d)
@@ -351,10 +385,10 @@ namespace MapControl
var rect = MapProjection.BoundingBoxToRect(boundingBox);
var center = new Point(rect.X + rect.Width / 2d, rect.Y + rect.Height / 2d);
var scale = Math.Min(RenderSize.Width / rect.Width, RenderSize.Height / rect.Height)
- * MapProjection.TrueScale * 360d / 256d;
+ * MapProjection.Wgs84MetersPerDegree * 360d / 256d;
TargetZoomLevel = Math.Log(scale, 2d);
- TargetCenter = MapProjection.PointToLocation(center);
+ TargetCenter = MapProjection.MapToLocation(center);
TargetHeading = 0d;
}
@@ -669,13 +703,16 @@ namespace MapControl
private void UpdateTransform(bool resetTransformCenter = false, bool projectionChanged = false)
{
var projection = MapProjection;
+ var viewportScale = 256d * Math.Pow(2d, ZoomLevel) / (360d * MapProjection.Wgs84MetersPerDegree);
var center = transformCenter ?? Center;
- projection.SetViewportTransform(ProjectionCenter ?? Center, center, viewportCenter, ZoomLevel, Heading);
+ projection.Center = ProjectionCenter ?? Center;
+
+ ViewTransform.SetTransform(projection.LocationToMap(center), viewportCenter, viewportScale, Heading);
if (transformCenter != null)
{
- center = projection.ViewportPointToLocation(new Point(RenderSize.Width / 2d, RenderSize.Height / 2d));
+ center = ViewportPointToLocation(new Point(RenderSize.Width / 2d, RenderSize.Height / 2d));
center.Longitude = Location.NormalizeLongitude(center.Longitude);
if (center.Latitude < -projection.MaxLatitude || center.Latitude > projection.MaxLatitude)
@@ -694,17 +731,20 @@ namespace MapControl
if (resetTransformCenter)
{
ResetTransformCenter();
- projection.SetViewportTransform(ProjectionCenter ?? center, center, viewportCenter, ZoomLevel, Heading);
+
+ projection.Center = ProjectionCenter ?? center;
+
+ ViewTransform.SetTransform(projection.LocationToMap(center), viewportCenter, viewportScale, Heading);
}
}
- ViewportTransform.Matrix = projection.ViewportTransform;
+ ViewportTransform.Matrix = ViewTransform.MapToViewMatrix;
- var scale = projection.GetMapScale(center);
- ScaleTransform.ScaleX = scale.X;
- ScaleTransform.ScaleY = scale.Y;
+ var scale = projection.GetRelativeScale(center);
+ ScaleTransform.ScaleX = scale.X * ViewTransform.Scale;
+ ScaleTransform.ScaleY = scale.Y * ViewTransform.Scale;
- RotateTransform.Angle = Heading;
+ RotateTransform.Angle = ViewTransform.Rotation;
OnViewportChanged(new ViewportChangedEventArgs(projectionChanged, Center.Longitude - centerLongitude));
diff --git a/MapControl/Shared/MapGraticule.cs b/MapControl/Shared/MapGraticule.cs
index 316aa81a..b06bfef1 100644
--- a/MapControl/Shared/MapGraticule.cs
+++ b/MapControl/Shared/MapGraticule.cs
@@ -30,7 +30,7 @@ namespace MapControl
private double GetLineDistance()
{
- var pixelPerDegree = ParentMap.MapProjection.ViewportScale * ParentMap.MapProjection.TrueScale;
+ var pixelPerDegree = ParentMap.ViewTransform.Scale * ParentMap.MapProjection.UnitsPerDegree;
var minDistance = MinLineDistance / pixelPerDegree;
var scale = 1d;
diff --git a/MapControl/Shared/MapImageLayer.cs b/MapControl/Shared/MapImageLayer.cs
index 574a3899..a8f92ba7 100644
--- a/MapControl/Shared/MapImageLayer.cs
+++ b/MapControl/Shared/MapImageLayer.cs
@@ -258,7 +258,7 @@ namespace MapControl
var y = (ParentMap.RenderSize.Height - height) / 2d;
var rect = new Rect(x, y, width, height);
- BoundingBox = ParentMap.MapProjection.ViewportRectToBoundingBox(rect);
+ BoundingBox = ParentMap.ViewportRectToBoundingBox(rect);
if (BoundingBox != null)
{
diff --git a/MapControl/Shared/MapPanel.cs b/MapControl/Shared/MapPanel.cs
index 8941efe3..8b400c4c 100644
--- a/MapControl/Shared/MapPanel.cs
+++ b/MapControl/Shared/MapPanel.cs
@@ -145,14 +145,13 @@ namespace MapControl
private Point ArrangeElement(FrameworkElement element, Location location)
{
- var projection = parentMap.MapProjection;
- var pos = projection.LocationToViewportPoint(location);
+ var pos = parentMap.LocationToViewportPoint(location);
- if (projection.IsNormalCylindrical &&
+ if (parentMap.MapProjection.IsNormalCylindrical &&
(pos.X < 0d || pos.X > parentMap.RenderSize.Width ||
pos.Y < 0d || pos.Y > parentMap.RenderSize.Height))
{
- pos = projection.LocationToViewportPoint(new Location(
+ pos = parentMap.LocationToViewportPoint(new Location(
location.Latitude,
Location.NearestLongitude(location.Longitude, parentMap.Center.Longitude)));
}
@@ -202,20 +201,20 @@ namespace MapControl
var projection = parentMap.MapProjection;
var rect = projection.BoundingBoxToRect(boundingBox);
var center = new Point(rect.X + rect.Width / 2d, rect.Y + rect.Height / 2d);
- var pos = projection.ViewportTransform.Transform(center);
+ var pos = parentMap.ViewTransform.MapToView(center);
if (projection.IsNormalCylindrical &&
(pos.X < 0d || pos.X > parentMap.RenderSize.Width ||
pos.Y < 0d || pos.Y > parentMap.RenderSize.Height))
{
- var location = projection.PointToLocation(center);
+ var location = projection.MapToLocation(center);
location.Longitude = Location.NearestLongitude(location.Longitude, parentMap.Center.Longitude);
- pos = projection.LocationToViewportPoint(location);
+ pos = parentMap.LocationToViewportPoint(location);
}
- rect.Width *= projection.ViewportScale;
- rect.Height *= projection.ViewportScale;
+ rect.Width *= parentMap.ViewTransform.Scale;
+ rect.Height *= parentMap.ViewTransform.Scale;
rect.X = pos.X - rect.Width / 2d;
rect.Y = pos.Y - rect.Height / 2d;
diff --git a/MapControl/Shared/MapProjection.cs b/MapControl/Shared/MapProjection.cs
index 125e85e4..f13fb09b 100644
--- a/MapControl/Shared/MapProjection.cs
+++ b/MapControl/Shared/MapProjection.cs
@@ -6,24 +6,26 @@ using System;
using System.Globalization;
#if WINDOWS_UWP
using Windows.Foundation;
-using Windows.UI.Xaml.Media;
#else
using System.Windows;
-using System.Windows.Media;
#endif
namespace MapControl
{
///
- /// Defines a map projection between geographic coordinates, cartesian map coordinates and viewport coordinates.
+ /// Defines a map projection between geographic coordinates and cartesian map coordinates.
///
public abstract class MapProjection
{
public const double Wgs84EquatorialRadius = 6378137d;
+ public const double Wgs84MetersPerDegree = Wgs84EquatorialRadius * Math.PI / 180d;
public const double Wgs84Flattening = 1d / 298.257223563;
public static readonly double Wgs84Eccentricity = Math.Sqrt((2d - Wgs84Flattening) * Wgs84Flattening);
- public const double Wgs84MetersPerDegree = Wgs84EquatorialRadius * Math.PI / 180d;
+ ///
+ /// Gets or sets the projection center. Only relevant for azimuthal projections.
+ ///
+ public Location Center { get; set; } = new Location();
///
/// Gets or sets the WMS 1.3.0 CRS identifier.
@@ -58,54 +60,28 @@ namespace MapControl
/// Gets the scale factor from geographic to cartesian coordinates, on the line of true scale of a
/// cylindrical projection (usually the equator), or at the projection center of an azimuthal projection.
///
- public virtual double TrueScale
+ public virtual double UnitsPerDegree
{
get { return Wgs84MetersPerDegree; }
}
///
- /// Gets the projection center. Only relevant for azimuthal projections.
+ /// Gets the relative map scale at the specified Location.
///
- public Location ProjectionCenter { get; private set; } = new Location();
-
- ///
- /// Gets the transform matrix from cartesian map coordinates to viewport coordinates.
- ///
- public Matrix ViewportTransform { get; private set; }
-
- ///
- /// Gets the transform matrix from viewport coordinates to cartesian map coordinates.
- ///
- public Matrix InverseViewportTransform { get; private set; }
-
- ///
- /// Gets the rotation angle of the ViewportTransform matrix.
- ///
- public double ViewportRotation { get; private set; }
-
- ///
- /// Gets the scaling factor from cartesian map coordinates to viewport coordinates
- /// at the projection's point of true scale.
- ///
- public double ViewportScale { get; private set; }
-
- ///
- /// Gets the map scale at the specified Location as viewport coordinate units per meter (px/m).
- ///
- public virtual Vector GetMapScale(Location location)
+ public virtual Vector GetRelativeScale(Location location)
{
- return new Vector(ViewportScale, ViewportScale);
+ return new Vector(1, 1);
}
///
/// Transforms a Location in geographic coordinates to a Point in cartesian map coordinates.
///
- public abstract Point LocationToPoint(Location location);
+ public abstract Point LocationToMap(Location location);
///
/// Transforms a Point in cartesian map coordinates to a Location in geographic coordinates.
///
- public abstract Location PointToLocation(Point point);
+ public abstract Location MapToLocation(Point point);
///
/// Transforms a BoundingBox in geographic coordinates to a Rect in cartesian map coordinates.
@@ -113,8 +89,8 @@ namespace MapControl
public virtual Rect BoundingBoxToRect(BoundingBox boundingBox)
{
return new Rect(
- LocationToPoint(new Location(boundingBox.South, boundingBox.West)),
- LocationToPoint(new Location(boundingBox.North, boundingBox.East)));
+ LocationToMap(new Location(boundingBox.South, boundingBox.West)),
+ LocationToMap(new Location(boundingBox.North, boundingBox.East)));
}
///
@@ -122,53 +98,19 @@ namespace MapControl
///
public virtual BoundingBox RectToBoundingBox(Rect rect)
{
- var sw = PointToLocation(new Point(rect.X, rect.Y));
- var ne = PointToLocation(new Point(rect.X + rect.Width, rect.Y + rect.Height));
+ var sw = MapToLocation(new Point(rect.X, rect.Y));
+ var ne = MapToLocation(new Point(rect.X + rect.Width, rect.Y + rect.Height));
return new BoundingBox(sw.Latitude, sw.Longitude, ne.Latitude, ne.Longitude);
}
- ///
- /// Transforms a Location in geographic coordinates to a Point in viewport coordinates.
- ///
- public Point LocationToViewportPoint(Location location)
- {
- return ViewportTransform.Transform(LocationToPoint(location));
- }
-
- ///
- /// Transforms a Point in viewport coordinates to a Location in geographic coordinates.
- ///
- public Location ViewportPointToLocation(Point point)
- {
- return PointToLocation(InverseViewportTransform.Transform(point));
- }
-
- ///
- /// Transforms a Rect in viewport coordinates to a BoundingBox in geographic coordinates.
- ///
- public BoundingBox ViewportRectToBoundingBox(Rect rect)
- {
- var p1 = InverseViewportTransform.Transform(new Point(rect.X, rect.Y));
- var p2 = InverseViewportTransform.Transform(new Point(rect.X, rect.Y + rect.Height));
- var p3 = InverseViewportTransform.Transform(new Point(rect.X + rect.Width, rect.Y));
- var p4 = InverseViewportTransform.Transform(new Point(rect.X + rect.Width, rect.Y + rect.Height));
-
- rect.X = Math.Min(p1.X, Math.Min(p2.X, Math.Min(p3.X, p4.X)));
- rect.Y = Math.Min(p1.Y, Math.Min(p2.Y, Math.Min(p3.Y, p4.Y)));
- rect.Width = Math.Max(p1.X, Math.Max(p2.X, Math.Max(p3.X, p4.X))) - rect.X;
- rect.Height = Math.Max(p1.Y, Math.Max(p2.Y, Math.Max(p3.Y, p4.Y))) - rect.Y;
-
- return RectToBoundingBox(rect);
- }
-
///
/// Gets the CRS parameter value for a WMS GetMap request.
///
public virtual string GetCrsValue()
{
return CrsId.StartsWith("AUTO:") || CrsId.StartsWith("AUTO2:")
- ? string.Format(CultureInfo.InvariantCulture, "{0},1,{1},{2}", CrsId, ProjectionCenter.Longitude, ProjectionCenter.Latitude)
+ ? string.Format(CultureInfo.InvariantCulture, "{0},1,{1},{2}", CrsId, Center.Longitude, Center.Latitude)
: CrsId;
}
@@ -180,78 +122,5 @@ namespace MapControl
return string.Format(CultureInfo.InvariantCulture,
"{0},{1},{2},{3}", rect.X, rect.Y, (rect.X + rect.Width), (rect.Y + rect.Height));
}
-
- ///
- /// Sets ProjectionCenter, ViewportScale, ViewportRotation, ViewportTransform and InverseViewportTransform.
- ///
- public void SetViewportTransform(Location projectionCenter, Location mapCenter, Point viewportCenter, double zoomLevel, double rotation)
- {
- ProjectionCenter = projectionCenter;
- ViewportScale = 256d * Math.Pow(2d, zoomLevel) / (360d * TrueScale);
- ViewportRotation = rotation;
-
- var center = LocationToPoint(mapCenter);
- var matrix = CreateViewportTransform(center, viewportCenter);
-
- ViewportTransform = matrix;
- matrix.Invert();
- InverseViewportTransform = matrix;
- }
-
- private Matrix CreateViewportTransform(Point mapCenter, Point viewportCenter)
- {
- var matrix = new Matrix(ViewportScale, 0d, 0d, -ViewportScale, -ViewportScale * mapCenter.X, ViewportScale * mapCenter.Y);
-
- matrix.Rotate(ViewportRotation);
- matrix.Translate(viewportCenter.X, viewportCenter.Y);
-
- return matrix;
- }
-
- internal Matrix CreateTileLayerTransform(double tileGridScale, Point tileGridTopLeft, Point tileGridOrigin)
- {
- var scale = ViewportScale / tileGridScale;
- var matrix = new Matrix(scale, 0d, 0d, scale, 0d, 0d);
-
- matrix.Rotate(ViewportRotation);
-
- // tile grid origin in map coordinates
- //
- var mapOrigin = new Point(
- tileGridTopLeft.X + tileGridOrigin.X / tileGridScale,
- tileGridTopLeft.Y - tileGridOrigin.Y / tileGridScale);
-
- // tile grid origin in viewport coordinates
- //
- var viewOrigin = ViewportTransform.Transform(mapOrigin);
-
- matrix.Translate(viewOrigin.X, viewOrigin.Y);
-
- return matrix;
- }
-
- internal Rect GetTileBounds(double tileGridScale, Point tileGridTopLeft, Size viewportSize)
- {
- var scale = tileGridScale / ViewportScale;
- var matrix = new Matrix(scale, 0d, 0d, scale, 0d, 0d);
-
- matrix.Rotate(-ViewportRotation);
-
- // viewport origin in map coordinates
- //
- var origin = InverseViewportTransform.Transform(new Point());
-
- // translate origin to tile grid origin in pixels
- //
- matrix.Translate(
- tileGridScale * (origin.X - tileGridTopLeft.X),
- tileGridScale * (tileGridTopLeft.Y - origin.Y));
-
- // transforms viewport bounds to tile pixel bounds
- //
- var transform = new MatrixTransform { Matrix = matrix };
-
- return transform.TransformBounds(new Rect(0d, 0d, viewportSize.Width, viewportSize.Height));
- }
}
}
diff --git a/MapControl/Shared/MapShape.cs b/MapControl/Shared/MapShape.cs
index aaeb9bb3..3ef8fc01 100644
--- a/MapControl/Shared/MapShape.cs
+++ b/MapControl/Shared/MapShape.cs
@@ -81,9 +81,9 @@ namespace MapControl
MapPanel.InitMapElement(this);
}
- protected Point LocationToPoint(Location location)
+ protected Point LocationToMap(Location location)
{
- var point = parentMap.MapProjection.LocationToPoint(location);
+ var point = parentMap.MapProjection.LocationToMap(location);
if (point.Y == double.PositiveInfinity)
{
@@ -99,7 +99,7 @@ namespace MapControl
protected Point LocationToViewportPoint(Location location)
{
- return parentMap.MapProjection.ViewportTransform.Transform(LocationToPoint(location));
+ return parentMap.ViewTransform.MapToView(LocationToMap(location));
}
protected double GetLongitudeOffset()
diff --git a/MapControl/Shared/MapTileLayer.cs b/MapControl/Shared/MapTileLayer.cs
index 8432c8c2..6aa6a7d4 100644
--- a/MapControl/Shared/MapTileLayer.cs
+++ b/MapControl/Shared/MapTileLayer.cs
@@ -23,10 +23,10 @@ namespace MapControl
{
public const int TileSize = 256;
- public static readonly Point TileGridTopLeft = new Point(
+ public static readonly Point TileMatrixTopLeft = new Point(
-180d * MapProjection.Wgs84MetersPerDegree, 180d * MapProjection.Wgs84MetersPerDegree);
- public static double TileGridScale(int zoomLevel)
+ public static double TileMatrixScale(int zoomLevel)
{
return (TileSize << zoomLevel) / (360d * MapProjection.Wgs84MetersPerDegree);
}
@@ -64,7 +64,7 @@ namespace MapControl
{
}
- public TileGrid TileGrid { get; private set; }
+ public TileMatrix TileMatrix { get; private set; }
public IReadOnlyCollection Tiles { get; private set; } = new List();
@@ -88,7 +88,7 @@ namespace MapControl
protected override void TileSourcePropertyChanged()
{
- if (TileGrid != null)
+ if (TileMatrix != null)
{
Tiles = new List();
UpdateTiles();
@@ -101,10 +101,10 @@ namespace MapControl
if (ParentMap == null || !ParentMap.MapProjection.IsWebMercator)
{
- TileGrid = null;
+ TileMatrix = null;
UpdateTiles();
}
- else if (SetTileGrid())
+ else if (SetTileMatrix())
{
SetRenderTransform();
UpdateTiles();
@@ -113,22 +113,22 @@ namespace MapControl
protected override void SetRenderTransform()
{
- // tile grid origin in pixels
+ // tile matrix origin in pixels
//
- var tileGridOrigin = new Point(TileSize * TileGrid.XMin, TileSize * TileGrid.YMin);
+ var tileMatrixOrigin = new Point(TileSize * TileMatrix.XMin, TileSize * TileMatrix.YMin);
- ((MatrixTransform)RenderTransform).Matrix = ParentMap.MapProjection.CreateTileLayerTransform(
- TileGridScale(TileGrid.ZoomLevel), TileGridTopLeft, tileGridOrigin);
+ ((MatrixTransform)RenderTransform).Matrix = ParentMap.ViewTransform.GetTileLayerTransform(
+ TileMatrixScale(TileMatrix.ZoomLevel), TileMatrixTopLeft, tileMatrixOrigin);
}
- private bool SetTileGrid()
+ private bool SetTileMatrix()
{
- var tileGridZoomLevel = (int)Math.Floor(ParentMap.ZoomLevel + 0.001); // avoid rounding issues
+ var tileMatrixZoomLevel = (int)Math.Floor(ParentMap.ZoomLevel + 0.001); // avoid rounding issues
// bounds in tile pixels from viewport size
//
- var tileBounds = ParentMap.MapProjection.GetTileBounds(
- TileGridScale(tileGridZoomLevel), TileGridTopLeft, ParentMap.RenderSize);
+ var tileBounds = ParentMap.ViewTransform.GetTileMatrixBounds(
+ TileMatrixScale(tileMatrixZoomLevel), TileMatrixTopLeft, ParentMap.RenderSize);
// tile column and row index bounds
//
@@ -137,15 +137,15 @@ namespace MapControl
var xMax = (int)Math.Floor((tileBounds.X + tileBounds.Width) / TileSize);
var yMax = (int)Math.Floor((tileBounds.Y + tileBounds.Height) / TileSize);
- if (TileGrid != null &&
- TileGrid.ZoomLevel == tileGridZoomLevel &&
- TileGrid.XMin == xMin && TileGrid.YMin == yMin &&
- TileGrid.XMax == xMax && TileGrid.YMax == yMax)
+ if (TileMatrix != null &&
+ TileMatrix.ZoomLevel == tileMatrixZoomLevel &&
+ TileMatrix.XMin == xMin && TileMatrix.YMin == yMin &&
+ TileMatrix.XMax == xMax && TileMatrix.YMax == yMax)
{
return false;
}
- TileGrid = new TileGrid(tileGridZoomLevel, xMin, yMin, xMax, yMax);
+ TileMatrix = new TileMatrix(tileMatrixZoomLevel, xMin, yMin, xMax, yMax);
return true;
}
@@ -154,9 +154,9 @@ namespace MapControl
{
var newTiles = new List();
- if (ParentMap != null && TileGrid != null && TileSource != null)
+ if (ParentMap != null && TileMatrix != null && TileSource != null)
{
- var maxZoomLevel = Math.Min(TileGrid.ZoomLevel, MaxZoomLevel);
+ var maxZoomLevel = Math.Min(TileMatrix.ZoomLevel, MaxZoomLevel);
if (maxZoomLevel >= MinZoomLevel)
{
@@ -164,16 +164,16 @@ namespace MapControl
if (this == ParentMap.MapLayer) // load background tiles
{
- minZoomLevel = Math.Max(TileGrid.ZoomLevel - MaxBackgroundLevels, MinZoomLevel);
+ minZoomLevel = Math.Max(TileMatrix.ZoomLevel - MaxBackgroundLevels, MinZoomLevel);
}
for (var z = minZoomLevel; z <= maxZoomLevel; z++)
{
- var tileSize = 1 << (TileGrid.ZoomLevel - z);
- var x1 = (int)Math.Floor((double)TileGrid.XMin / tileSize); // may be negative
- var x2 = TileGrid.XMax / tileSize;
- var y1 = Math.Max(TileGrid.YMin / tileSize, 0);
- var y2 = Math.Min(TileGrid.YMax / tileSize, (1 << z) - 1);
+ var tileSize = 1 << (TileMatrix.ZoomLevel - z);
+ var x1 = (int)Math.Floor((double)TileMatrix.XMin / tileSize); // may be negative
+ var x2 = TileMatrix.XMax / tileSize;
+ var y1 = Math.Max(TileMatrix.YMin / tileSize, 0);
+ var y2 = Math.Min(TileMatrix.YMax / tileSize, (1 << z) - 1);
for (var y = y1; y <= y2; y++)
{
@@ -227,13 +227,13 @@ namespace MapControl
protected override Size ArrangeOverride(Size finalSize)
{
- if (TileGrid != null)
+ if (TileMatrix != null)
{
foreach (var tile in Tiles)
{
- var tileSize = TileSize << (TileGrid.ZoomLevel - tile.ZoomLevel);
- var x = tileSize * tile.X - TileSize * TileGrid.XMin;
- var y = tileSize * tile.Y - TileSize * TileGrid.YMin;
+ var tileSize = TileSize << (TileMatrix.ZoomLevel - tile.ZoomLevel);
+ var x = tileSize * tile.X - TileSize * TileMatrix.XMin;
+ var y = tileSize * tile.Y - TileSize * TileMatrix.YMin;
tile.Image.Width = tileSize;
tile.Image.Height = tileSize;
diff --git a/MapControl/Shared/OrthographicProjection.cs b/MapControl/Shared/OrthographicProjection.cs
index ea389fda..595669d5 100644
--- a/MapControl/Shared/OrthographicProjection.cs
+++ b/MapControl/Shared/OrthographicProjection.cs
@@ -19,31 +19,31 @@ namespace MapControl
CrsId = "AUTO2:42003";
}
- public override Point LocationToPoint(Location location)
+ public override Point LocationToMap(Location location)
{
- if (location.Equals(ProjectionCenter))
+ if (location.Equals(Center))
{
return new Point();
}
- var lat0 = ProjectionCenter.Latitude * Math.PI / 180d;
+ var lat0 = Center.Latitude * Math.PI / 180d;
var lat = location.Latitude * Math.PI / 180d;
- var dLon = (location.Longitude - ProjectionCenter.Longitude) * Math.PI / 180d;
- var s = TrueScale * 180d / Math.PI;
+ var dLon = (location.Longitude - Center.Longitude) * Math.PI / 180d;
+ var s = UnitsPerDegree * 180d / Math.PI;
return new Point(
s * Math.Cos(lat) * Math.Sin(dLon),
s * (Math.Cos(lat0) * Math.Sin(lat) - Math.Sin(lat0) * Math.Cos(lat) * Math.Cos(dLon)));
}
- public override Location PointToLocation(Point point)
+ public override Location MapToLocation(Point point)
{
if (point.X == 0d && point.Y == 0d)
{
- return new Location(ProjectionCenter.Latitude, ProjectionCenter.Longitude);
+ return new Location(Center.Latitude, Center.Longitude);
}
- var s = TrueScale * 180d / Math.PI;
+ var s = UnitsPerDegree * 180d / Math.PI;
var x = point.X / s;
var y = point.Y / s;
var r2 = x * x + y * y;
@@ -57,13 +57,13 @@ namespace MapControl
var sinC = r;
var cosC = Math.Sqrt(1 - r2);
- var lat0 = ProjectionCenter.Latitude * Math.PI / 180d;
+ var lat0 = Center.Latitude * Math.PI / 180d;
var cosLat0 = Math.Cos(lat0);
var sinLat0 = Math.Sin(lat0);
return new Location(
180d / Math.PI * Math.Asin(cosC * sinLat0 + y * sinC * cosLat0 / r),
- 180d / Math.PI * Math.Atan2(x * sinC, r * cosC * cosLat0 - y * sinC * sinLat0) + ProjectionCenter.Longitude);
+ 180d / Math.PI * Math.Atan2(x * sinC, r * cosC * cosLat0 - y * sinC * sinLat0) + Center.Longitude);
}
}
}
diff --git a/MapControl/Shared/StereographicProjection.cs b/MapControl/Shared/StereographicProjection.cs
index 425e5c02..dd442cc6 100644
--- a/MapControl/Shared/StereographicProjection.cs
+++ b/MapControl/Shared/StereographicProjection.cs
@@ -19,35 +19,35 @@ namespace MapControl
CrsId = "AUTO2:97002"; // GeoServer non-standard CRS ID
}
- public override Point LocationToPoint(Location location)
+ public override Point LocationToMap(Location location)
{
- if (location.Equals(ProjectionCenter))
+ if (location.Equals(Center))
{
return new Point();
}
double azimuth, distance;
- GetAzimuthDistance(ProjectionCenter, location, out azimuth, out distance);
+ GetAzimuthDistance(Center, location, out azimuth, out distance);
- var mapDistance = Math.Tan(distance / 2d) * 2d * TrueScale * 180d / Math.PI;
+ var mapDistance = Math.Tan(distance / 2d) * 2d * UnitsPerDegree * 180d / Math.PI;
return new Point(mapDistance * Math.Sin(azimuth), mapDistance * Math.Cos(azimuth));
}
- public override Location PointToLocation(Point point)
+ public override Location MapToLocation(Point point)
{
if (point.X == 0d && point.Y == 0d)
{
- return new Location(ProjectionCenter.Latitude, ProjectionCenter.Longitude);
+ 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 distance = 2d * Math.Atan(mapDistance / (2d * TrueScale * 180d / Math.PI));
+ var distance = 2d * Math.Atan(mapDistance / (2d * UnitsPerDegree * 180d / Math.PI));
- return GetLocation(ProjectionCenter, azimuth, distance);
+ return GetLocation(Center, azimuth, distance);
}
}
}
diff --git a/MapControl/Shared/TileGrid.cs b/MapControl/Shared/TileMatrix.cs
similarity index 82%
rename from MapControl/Shared/TileGrid.cs
rename to MapControl/Shared/TileMatrix.cs
index 825efbb9..ef786815 100644
--- a/MapControl/Shared/TileGrid.cs
+++ b/MapControl/Shared/TileMatrix.cs
@@ -4,7 +4,7 @@
namespace MapControl
{
- public class TileGrid
+ public class TileMatrix
{
public readonly int ZoomLevel;
public readonly int XMin;
@@ -12,7 +12,7 @@ namespace MapControl
public readonly int XMax;
public readonly int YMax;
- public TileGrid(int zoomLevel, int xMin, int yMin, int xMax, int yMax)
+ public TileMatrix(int zoomLevel, int xMin, int yMin, int xMax, int yMax)
{
ZoomLevel = zoomLevel;
XMin = xMin;
diff --git a/MapControl/Shared/ViewTransform.cs b/MapControl/Shared/ViewTransform.cs
new file mode 100644
index 00000000..dd7ff6cd
--- /dev/null
+++ b/MapControl/Shared/ViewTransform.cs
@@ -0,0 +1,118 @@
+// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
+// © 2020 Clemens Fischer
+// Licensed under the Microsoft Public License (Ms-PL)
+
+#if WINDOWS_UWP
+using Windows.Foundation;
+using Windows.UI.Xaml.Media;
+#else
+using System.Windows;
+using System.Windows.Media;
+#endif
+
+namespace MapControl
+{
+ ///
+ /// Defines the transformation between cartesian map coordinates and viewport coordinates.
+ ///
+ public class ViewTransform
+ {
+ ///
+ /// Gets the transform matrix from cartesian map coordinates to viewport coordinates.
+ ///
+ public Matrix MapToViewMatrix { get; private set; }
+
+ ///
+ /// Gets the transform matrix from viewport coordinates to cartesian map coordinates.
+ ///
+ public Matrix ViewToMapMatrix { get; private set; }
+
+ ///
+ /// Gets the scaling factor from cartesian map coordinates to viewport coordinates.
+ ///
+ public double Scale { get; private set; }
+
+ ///
+ /// Gets the rotation angle of the transform matrix.
+ ///
+ public double Rotation { get; private set; }
+
+ ///
+ /// Transforms a Point from cartesian map coordinates to viewport coordinates.
+ ///
+ public Point MapToView(Point point)
+ {
+ return MapToViewMatrix.Transform(point);
+ }
+
+ ///
+ /// Transforms a Point from viewport coordinates to cartesian map coordinates.
+ ///
+ public Point ViewToMap(Point point)
+ {
+ return ViewToMapMatrix.Transform(point);
+ }
+
+ public void SetTransform(Point mapCenter, Point viewportCenter, double scale, double rotation)
+ {
+ Scale = scale;
+ Rotation = rotation;
+
+ var transform = new Matrix(Scale, 0d, 0d, -Scale, -Scale * mapCenter.X, Scale * mapCenter.Y);
+
+ transform.Rotate(Rotation);
+ transform.Translate(viewportCenter.X, viewportCenter.Y);
+
+ MapToViewMatrix = transform;
+
+ transform.Invert();
+
+ ViewToMapMatrix = transform;
+ }
+
+ public Matrix GetTileLayerTransform(double tileMatrixScale, Point tileMatrixTopLeft, Point tileMatrixOrigin)
+ {
+ var transformScale = Scale / tileMatrixScale;
+ var transform = new Matrix(transformScale, 0d, 0d, transformScale, 0d, 0d);
+
+ transform.Rotate(Rotation);
+
+ // tile matrix origin in map coordinates
+ //
+ var mapOrigin = new Point(
+ tileMatrixTopLeft.X + tileMatrixOrigin.X / tileMatrixScale,
+ tileMatrixTopLeft.Y - tileMatrixOrigin.Y / tileMatrixScale);
+
+ // tile matrix origin in viewport coordinates
+ //
+ var viewOrigin = MapToView(mapOrigin);
+
+ transform.Translate(viewOrigin.X, viewOrigin.Y);
+
+ return transform;
+ }
+
+ public Rect GetTileMatrixBounds(double tileMatrixScale, Point tileMatrixTopLeft, Size viewportSize)
+ {
+ var transformScale = tileMatrixScale / Scale;
+ var transform = new Matrix(transformScale, 0d, 0d, transformScale, 0d, 0d);
+
+ transform.Rotate(-Rotation);
+
+ // viewport origin in map coordinates
+ //
+ var origin = ViewToMap(new Point());
+
+ // translate origin to tile matrix origin in pixels
+ //
+ transform.Translate(
+ tileMatrixScale * (origin.X - tileMatrixTopLeft.X),
+ tileMatrixScale * (tileMatrixTopLeft.Y - origin.Y));
+
+ // transform viewport bounds to tile pixel bounds
+ //
+ return new MatrixTransform { Matrix = transform }
+ .TransformBounds(new Rect(0d, 0d, viewportSize.Width, viewportSize.Height));
+ }
+ }
+}
diff --git a/MapControl/Shared/WebMercatorProjection.cs b/MapControl/Shared/WebMercatorProjection.cs
index 68709a41..3fe98c26 100644
--- a/MapControl/Shared/WebMercatorProjection.cs
+++ b/MapControl/Shared/WebMercatorProjection.cs
@@ -32,25 +32,25 @@ namespace MapControl
get { return maxLatitude; }
}
- public override Vector GetMapScale(Location location)
+ public override Vector GetRelativeScale(Location location)
{
var k = 1d / Math.Cos(location.Latitude * Math.PI / 180d); // p.44 (7-3)
- return new Vector(ViewportScale * k, ViewportScale * k);
+ return new Vector(k, k);
}
- public override Point LocationToPoint(Location location)
+ public override Point LocationToMap(Location location)
{
return new Point(
- TrueScale * location.Longitude,
- TrueScale * LatitudeToY(location.Latitude));
+ UnitsPerDegree * location.Longitude,
+ UnitsPerDegree * LatitudeToY(location.Latitude));
}
- public override Location PointToLocation(Point point)
+ public override Location MapToLocation(Point point)
{
return new Location(
- YToLatitude(point.Y / TrueScale),
- point.X / TrueScale);
+ YToLatitude(point.Y / UnitsPerDegree),
+ point.X / UnitsPerDegree);
}
public static double LatitudeToY(double latitude)
diff --git a/MapControl/Shared/WmsImageLayer.cs b/MapControl/Shared/WmsImageLayer.cs
index 7d9f9d72..8e289304 100644
--- a/MapControl/Shared/WmsImageLayer.cs
+++ b/MapControl/Shared/WmsImageLayer.cs
@@ -142,8 +142,8 @@ namespace MapControl
uri += "&CRS=" + projection.GetCrsValue();
uri += "&BBOX=" + projection.GetBboxValue(rect);
- uri += "&WIDTH=" + (int)Math.Round(projection.ViewportScale * rect.Width);
- uri += "&HEIGHT=" + (int)Math.Round(projection.ViewportScale * rect.Height);
+ uri += "&WIDTH=" + (int)Math.Round(ParentMap.ViewTransform.Scale * rect.Width);
+ uri += "&HEIGHT=" + (int)Math.Round(ParentMap.ViewTransform.Scale * rect.Height);
}
return uri;
diff --git a/MapControl/Shared/WmtsTileLayer.cs b/MapControl/Shared/WmtsTileLayer.cs
index 8571ecad..464a05fd 100644
--- a/MapControl/Shared/WmtsTileLayer.cs
+++ b/MapControl/Shared/WmtsTileLayer.cs
@@ -104,14 +104,14 @@ namespace MapControl
{
foreach (var layer in ChildLayers)
{
- layer.SetRenderTransform(ParentMap.MapProjection);
+ layer.SetRenderTransform(ParentMap.ViewTransform);
}
}
private bool UpdateChildLayers(WmtsTileMatrixSet tileMatrixSet)
{
var layersChanged = false;
- var maxScale = 1.001 * ParentMap.MapProjection.ViewportScale; // avoid rounding issues
+ var maxScale = 1.001 * ParentMap.ViewTransform.Scale; // avoid rounding issues
// show all TileMatrix layers with Scale <= maxScale, at least the first layer
//
@@ -142,7 +142,7 @@ namespace MapControl
layersChanged = true;
}
- if (layer.SetBounds(ParentMap.MapProjection, ParentMap.RenderSize))
+ if (layer.SetBounds(ParentMap.ViewTransform, ParentMap.RenderSize))
{
layersChanged = true;
}
diff --git a/MapControl/Shared/WmtsTileMatrixLayer.cs b/MapControl/Shared/WmtsTileMatrixLayer.cs
index c12422c3..bf2c881b 100644
--- a/MapControl/Shared/WmtsTileMatrixLayer.cs
+++ b/MapControl/Shared/WmtsTileMatrixLayer.cs
@@ -37,21 +37,21 @@ namespace MapControl
public IReadOnlyCollection Tiles { get; private set; } = new List();
- public void SetRenderTransform(MapProjection projection)
+ public void SetRenderTransform(ViewTransform viewTransform)
{
- // tile grid origin in pixels
+ // tile matrix origin in pixels
//
- var tileGridOrigin = new Point(TileMatrix.TileWidth * XMin, TileMatrix.TileHeight * YMin);
+ var tileMatrixOrigin = new Point(TileMatrix.TileWidth * XMin, TileMatrix.TileHeight * YMin);
((MatrixTransform)RenderTransform).Matrix =
- projection.CreateTileLayerTransform(TileMatrix.Scale, TileMatrix.TopLeft, tileGridOrigin);
+ viewTransform.GetTileLayerTransform(TileMatrix.Scale, TileMatrix.TopLeft, tileMatrixOrigin);
}
- public bool SetBounds(MapProjection projection, Size viewportSize)
+ public bool SetBounds(ViewTransform viewTransform, Size viewportSize)
{
// bounds in tile pixels from viewport size
//
- var bounds = projection.GetTileBounds(TileMatrix.Scale, TileMatrix.TopLeft, viewportSize);
+ var bounds = viewTransform.GetTileMatrixBounds(TileMatrix.Scale, TileMatrix.TopLeft, viewportSize);
// tile column and row index bounds
//
diff --git a/MapControl/Shared/WorldMercatorProjection.cs b/MapControl/Shared/WorldMercatorProjection.cs
index 75391582..590cd835 100644
--- a/MapControl/Shared/WorldMercatorProjection.cs
+++ b/MapControl/Shared/WorldMercatorProjection.cs
@@ -30,27 +30,27 @@ namespace MapControl
get { return maxLatitude; }
}
- public override Vector GetMapScale(Location location)
+ public override Vector GetRelativeScale(Location location)
{
var lat = location.Latitude * Math.PI / 180d;
var eSinLat = Wgs84Eccentricity * Math.Sin(lat);
var k = Math.Sqrt(1d - eSinLat * eSinLat) / Math.Cos(lat); // p.44 (7-8)
- return new Vector(ViewportScale * k, ViewportScale * k);
+ return new Vector(k, k);
}
- public override Point LocationToPoint(Location location)
+ public override Point LocationToMap(Location location)
{
return new Point(
- TrueScale * location.Longitude,
- TrueScale * LatitudeToY(location.Latitude));
+ UnitsPerDegree * location.Longitude,
+ UnitsPerDegree * LatitudeToY(location.Latitude));
}
- public override Location PointToLocation(Point point)
+ public override Location MapToLocation(Point point)
{
return new Location(
- YToLatitude(point.Y / TrueScale),
- point.X / TrueScale);
+ YToLatitude(point.Y / UnitsPerDegree),
+ point.X / UnitsPerDegree);
}
public static double LatitudeToY(double latitude)
diff --git a/MapControl/UWP/MapBase.UWP.cs b/MapControl/UWP/MapBase.UWP.cs
index bad97e0b..b4b10375 100644
--- a/MapControl/UWP/MapBase.UWP.cs
+++ b/MapControl/UWP/MapBase.UWP.cs
@@ -45,10 +45,6 @@ namespace MapControl
public MapBase()
{
- MapProjection = new WebMercatorProjection();
- ScaleRotateTransform.Children.Add(ScaleTransform);
- ScaleRotateTransform.Children.Add(RotateTransform);
-
// set Background by Style to enable resetting by ClearValue in MapLayerPropertyChanged
var style = new Style(typeof(MapBase));
style.Setters.Add(new Setter(BackgroundProperty, new SolidColorBrush(Colors.Transparent)));
diff --git a/MapControl/UWP/MapControl.UWP.csproj b/MapControl/UWP/MapControl.UWP.csproj
index ed4d6bed..8760dd14 100644
--- a/MapControl/UWP/MapControl.UWP.csproj
+++ b/MapControl/UWP/MapControl.UWP.csproj
@@ -140,18 +140,21 @@
Tile.cs
-
- TileGrid.cs
-
TileImageLoader.cs
+
+ TileMatrix.cs
+
TileSource.cs
ViewportChangedEventArgs.cs
+
+ ViewTransform.cs
+
WebMercatorProjection.cs
diff --git a/MapControl/UWP/MapGraticule.UWP.cs b/MapControl/UWP/MapGraticule.UWP.cs
index a2875ca8..0725ea7e 100644
--- a/MapControl/UWP/MapGraticule.UWP.cs
+++ b/MapControl/UWP/MapGraticule.UWP.cs
@@ -24,7 +24,8 @@ namespace MapControl
protected override void OnViewportChanged(ViewportChangedEventArgs e)
{
- var projection = ParentMap.MapProjection;
+ var map = ParentMap;
+ var projection = map.MapProjection;
if (projection.IsNormalCylindrical)
{
@@ -39,7 +40,7 @@ namespace MapControl
Children.Add(path);
}
- var bounds = projection.ViewportRectToBoundingBox(new Rect(0d, 0d, ParentMap.RenderSize.Width, ParentMap.RenderSize.Height));
+ var bounds = map.ViewportRectToBoundingBox(new Rect(0d, 0d, map.RenderSize.Width, map.RenderSize.Height));
var lineDistance = GetLineDistance();
var labelStart = new Location(
@@ -65,14 +66,14 @@ namespace MapControl
{
var figure = new PathFigure
{
- StartPoint = projection.LocationToViewportPoint(new Location(lat, lineStart.Longitude)),
+ StartPoint = map.LocationToViewportPoint(new Location(lat, lineStart.Longitude)),
IsClosed = false,
IsFilled = false
};
figure.Segments.Add(new LineSegment
{
- Point = projection.LocationToViewportPoint(new Location(lat, lineEnd.Longitude))
+ Point = map.LocationToViewportPoint(new Location(lat, lineEnd.Longitude))
});
geometry.Figures.Add(figure);
@@ -82,14 +83,14 @@ namespace MapControl
{
var figure = new PathFigure
{
- StartPoint = projection.LocationToViewportPoint(new Location(lineStart.Latitude, lon)),
+ StartPoint = map.LocationToViewportPoint(new Location(lineStart.Latitude, lon)),
IsClosed = false,
IsFilled = false
};
figure.Segments.Add(new LineSegment
{
- Point = projection.LocationToViewportPoint(new Location(lineEnd.Latitude, lon))
+ Point = map.LocationToViewportPoint(new Location(lineEnd.Latitude, lon))
});
geometry.Figures.Add(figure);
@@ -112,7 +113,7 @@ namespace MapControl
{
var renderTransform = new TransformGroup();
renderTransform.Children.Add(new TranslateTransform());
- renderTransform.Children.Add(ParentMap.RotateTransform);
+ renderTransform.Children.Add(map.RotateTransform);
renderTransform.Children.Add(new TranslateTransform());
label = new TextBlock { RenderTransform = renderTransform };
@@ -153,7 +154,7 @@ namespace MapControl
var label = (TextBlock)Children[i];
var location = (Location)label.Tag;
var viewportTransform = (TranslateTransform)((TransformGroup)label.RenderTransform).Children[2];
- var viewportPosition = projection.LocationToViewportPoint(location);
+ var viewportPosition = map.LocationToViewportPoint(location);
viewportTransform.X = viewportPosition.X;
viewportTransform.Y = viewportPosition.Y;
}
diff --git a/MapControl/UWP/Properties/AssemblyInfo.cs b/MapControl/UWP/Properties/AssemblyInfo.cs
index b54101d4..11355637 100644
--- a/MapControl/UWP/Properties/AssemblyInfo.cs
+++ b/MapControl/UWP/Properties/AssemblyInfo.cs
@@ -7,8 +7,8 @@ using System.Runtime.InteropServices;
[assembly: AssemblyCompany("Clemens Fischer")]
[assembly: AssemblyCopyright("Copyright © 2020 Clemens Fischer")]
[assembly: AssemblyTrademark("")]
-[assembly: AssemblyVersion("4.17.0")]
-[assembly: AssemblyFileVersion("4.17.0")]
+[assembly: AssemblyVersion("5.0.0")]
+[assembly: AssemblyFileVersion("5.0.0")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCulture("")]
[assembly: ComVisible(false)]
diff --git a/MapControl/WPF/MapBase.WPF.cs b/MapControl/WPF/MapBase.WPF.cs
index a3438fb3..66754f4c 100644
--- a/MapControl/WPF/MapBase.WPF.cs
+++ b/MapControl/WPF/MapBase.WPF.cs
@@ -53,13 +53,6 @@ namespace MapControl
BackgroundProperty.OverrideMetadata(typeof(MapBase), new FrameworkPropertyMetadata(Brushes.Transparent));
}
- public MapBase()
- {
- MapProjection = new WebMercatorProjection();
- ScaleRotateTransform.Children.Add(ScaleTransform);
- ScaleRotateTransform.Children.Add(RotateTransform);
- }
-
protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
{
base.OnRenderSizeChanged(sizeInfo);
diff --git a/MapControl/WPF/MapControl.WPF.csproj b/MapControl/WPF/MapControl.WPF.csproj
index da4b7d47..b46c21e3 100644
--- a/MapControl/WPF/MapControl.WPF.csproj
+++ b/MapControl/WPF/MapControl.WPF.csproj
@@ -9,7 +9,7 @@
..\..\MapControl.snk
false
XAML Map Control
- 4.17.0
+ 5.0.0
XAML Map Control Library
Clemens Fischer
Copyright © 2020 Clemens Fischer
diff --git a/MapControl/WPF/MapGraticule.WPF.cs b/MapControl/WPF/MapGraticule.WPF.cs
index 3dd81524..5200671c 100644
--- a/MapControl/WPF/MapGraticule.WPF.cs
+++ b/MapControl/WPF/MapGraticule.WPF.cs
@@ -46,7 +46,7 @@ namespace MapControl
if (projection.IsNormalCylindrical)
{
- DrawCylindricalGraticule(drawingContext, projection, lineDistance, labelFormat);
+ DrawCylindricalGraticule(drawingContext, lineDistance, labelFormat);
}
else
{
@@ -54,9 +54,9 @@ namespace MapControl
}
}
- private void DrawCylindricalGraticule(DrawingContext drawingContext, MapProjection projection, double lineDistance, string labelFormat)
+ private void DrawCylindricalGraticule(DrawingContext drawingContext, double lineDistance, string labelFormat)
{
- var boundingBox = projection.ViewportRectToBoundingBox(new Rect(ParentMap.RenderSize));
+ var boundingBox = ParentMap.ViewportRectToBoundingBox(new Rect(ParentMap.RenderSize));
var latLabelStart = Math.Ceiling(boundingBox.South / lineDistance) * lineDistance;
var lonLabelStart = Math.Ceiling(boundingBox.West / lineDistance) * lineDistance;
var latLabels = new List