mirror of
https://github.com/ClemensFischer/XAML-Map-Control.git
synced 2025-12-06 07:12:04 +01:00
Version 5.0: Separated map projection and view transform.
This commit is contained in:
parent
53723844a0
commit
c7cb2efcdb
|
|
@ -7,8 +7,8 @@ using System.Runtime.InteropServices;
|
||||||
[assembly: AssemblyCompany("Clemens Fischer")]
|
[assembly: AssemblyCompany("Clemens Fischer")]
|
||||||
[assembly: AssemblyCopyright("Copyright © 2020 Clemens Fischer")]
|
[assembly: AssemblyCopyright("Copyright © 2020 Clemens Fischer")]
|
||||||
[assembly: AssemblyTrademark("")]
|
[assembly: AssemblyTrademark("")]
|
||||||
[assembly: AssemblyVersion("4.17.0")]
|
[assembly: AssemblyVersion("5.0.0")]
|
||||||
[assembly: AssemblyFileVersion("4.17.0")]
|
[assembly: AssemblyFileVersion("5.0.0")]
|
||||||
[assembly: AssemblyConfiguration("")]
|
[assembly: AssemblyConfiguration("")]
|
||||||
[assembly: AssemblyCulture("")]
|
[assembly: AssemblyCulture("")]
|
||||||
[assembly: ComVisible(false)]
|
[assembly: ComVisible(false)]
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,8 @@ using System.Runtime.InteropServices;
|
||||||
[assembly: AssemblyCompany("Clemens Fischer")]
|
[assembly: AssemblyCompany("Clemens Fischer")]
|
||||||
[assembly: AssemblyCopyright("Copyright © 2020 Clemens Fischer")]
|
[assembly: AssemblyCopyright("Copyright © 2020 Clemens Fischer")]
|
||||||
[assembly: AssemblyTrademark("")]
|
[assembly: AssemblyTrademark("")]
|
||||||
[assembly: AssemblyVersion("4.17.0")]
|
[assembly: AssemblyVersion("5.0.0")]
|
||||||
[assembly: AssemblyFileVersion("4.17.0")]
|
[assembly: AssemblyFileVersion("5.0.0")]
|
||||||
[assembly: AssemblyConfiguration("")]
|
[assembly: AssemblyConfiguration("")]
|
||||||
[assembly: AssemblyCulture("")]
|
[assembly: AssemblyCulture("")]
|
||||||
[assembly: ComVisible(false)]
|
[assembly: ComVisible(false)]
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,8 @@ using System.Runtime.InteropServices;
|
||||||
[assembly: AssemblyCompany("Clemens Fischer")]
|
[assembly: AssemblyCompany("Clemens Fischer")]
|
||||||
[assembly: AssemblyCopyright("Copyright © 2020 Clemens Fischer")]
|
[assembly: AssemblyCopyright("Copyright © 2020 Clemens Fischer")]
|
||||||
[assembly: AssemblyTrademark("")]
|
[assembly: AssemblyTrademark("")]
|
||||||
[assembly: AssemblyVersion("4.17.0")]
|
[assembly: AssemblyVersion("5.0.0")]
|
||||||
[assembly: AssemblyFileVersion("4.17.0")]
|
[assembly: AssemblyFileVersion("5.0.0")]
|
||||||
[assembly: AssemblyConfiguration("")]
|
[assembly: AssemblyConfiguration("")]
|
||||||
[assembly: AssemblyCulture("")]
|
[assembly: AssemblyCulture("")]
|
||||||
[assembly: ComVisible(false)]
|
[assembly: ComVisible(false)]
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,8 @@ using System.Runtime.InteropServices;
|
||||||
[assembly: AssemblyCompany("Clemens Fischer")]
|
[assembly: AssemblyCompany("Clemens Fischer")]
|
||||||
[assembly: AssemblyCopyright("Copyright © 2020 Clemens Fischer")]
|
[assembly: AssemblyCopyright("Copyright © 2020 Clemens Fischer")]
|
||||||
[assembly: AssemblyTrademark("")]
|
[assembly: AssemblyTrademark("")]
|
||||||
[assembly: AssemblyVersion("4.17.0")]
|
[assembly: AssemblyVersion("5.0.0")]
|
||||||
[assembly: AssemblyFileVersion("4.17.0")]
|
[assembly: AssemblyFileVersion("5.0.0")]
|
||||||
[assembly: AssemblyConfiguration("")]
|
[assembly: AssemblyConfiguration("")]
|
||||||
[assembly: AssemblyCulture("")]
|
[assembly: AssemblyCulture("")]
|
||||||
[assembly: ComVisible(false)]
|
[assembly: ComVisible(false)]
|
||||||
|
|
|
||||||
|
|
@ -16,22 +16,22 @@ namespace MapControl
|
||||||
CrsId = "AUTO2:42004";
|
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(
|
return new Point(
|
||||||
xScale * (location.Longitude - ProjectionCenter.Longitude),
|
xScale * (location.Longitude - Center.Longitude),
|
||||||
Wgs84MetersPerDegree * location.Latitude);
|
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(
|
return new Location(
|
||||||
point.Y / Wgs84MetersPerDegree,
|
point.Y / UnitsPerDegree,
|
||||||
point.X / xScale + ProjectionCenter.Longitude);
|
point.X / xScale + Center.Longitude);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,35 +16,35 @@ namespace MapControl
|
||||||
{
|
{
|
||||||
// No standard CRS ID
|
// 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();
|
return new Point();
|
||||||
}
|
}
|
||||||
|
|
||||||
double azimuth, distance;
|
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));
|
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)
|
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 azimuth = Math.Atan2(point.X, point.Y);
|
||||||
var mapDistance = Math.Sqrt(point.X * point.X + point.Y * 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ namespace MapControl
|
||||||
|
|
||||||
if (cbbox != null)
|
if (cbbox != null)
|
||||||
{
|
{
|
||||||
var center = LocationToPoint(cbbox.Center);
|
var center = LocationToMap(cbbox.Center);
|
||||||
|
|
||||||
return new Rect(
|
return new Rect(
|
||||||
center.X - cbbox.Width / 2d, center.Y - cbbox.Height / 2d,
|
center.X - cbbox.Width / 2d, center.Y - cbbox.Height / 2d,
|
||||||
|
|
@ -39,7 +39,7 @@ namespace MapControl
|
||||||
|
|
||||||
public override BoundingBox RectToBoundingBox(Rect rect)
|
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
|
return new CenteredBoundingBox(center, rect.Width, rect.Height); // width and height in meters
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ namespace MapControl
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Equirectangular Projection.
|
/// 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.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class EquirectangularProjection : MapProjection
|
public class EquirectangularProjection : MapProjection
|
||||||
{
|
{
|
||||||
|
|
@ -23,33 +23,33 @@ namespace MapControl
|
||||||
CrsId = "EPSG:4326";
|
CrsId = "EPSG:4326";
|
||||||
}
|
}
|
||||||
|
|
||||||
public override double TrueScale
|
public override Vector GetRelativeScale(Location location)
|
||||||
{
|
|
||||||
get { return 1d; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public override Vector GetMapScale(Location location)
|
|
||||||
{
|
{
|
||||||
return new Vector(
|
return new Vector(
|
||||||
ViewportScale / (Wgs84MetersPerDegree * Math.Cos(location.Latitude * Math.PI / 180d)),
|
1d / Math.Cos(location.Latitude * Math.PI / 180d),
|
||||||
ViewportScale / Wgs84MetersPerDegree);
|
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)
|
public override string GetBboxValue(Rect rect)
|
||||||
{
|
{
|
||||||
return string.Format(CultureInfo.InvariantCulture,
|
return string.Format(CultureInfo.InvariantCulture,
|
||||||
CrsId != "CRS:84" ? "{1},{0},{3},{2}" : "{0},{1},{2},{3}",
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,37 +19,37 @@ namespace MapControl
|
||||||
CrsId = "AUTO2:97001"; // GeoServer non-standard CRS ID
|
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();
|
return new Point();
|
||||||
}
|
}
|
||||||
|
|
||||||
double azimuth, distance;
|
double azimuth, distance;
|
||||||
|
|
||||||
GetAzimuthDistance(ProjectionCenter, location, out azimuth, out distance);
|
GetAzimuthDistance(Center, location, out azimuth, out distance);
|
||||||
|
|
||||||
var mapDistance = distance < Math.PI / 2d
|
var mapDistance = distance < Math.PI / 2d
|
||||||
? Math.Tan(distance) * TrueScale * 180d / Math.PI
|
? Math.Tan(distance) * UnitsPerDegree * 180d / Math.PI
|
||||||
: double.PositiveInfinity;
|
: double.PositiveInfinity;
|
||||||
|
|
||||||
return new Point(mapDistance * Math.Sin(azimuth), mapDistance * Math.Cos(azimuth));
|
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)
|
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 azimuth = Math.Atan2(point.X, point.Y);
|
||||||
var mapDistance = Math.Sqrt(point.X * point.X + point.Y * 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
#if WINDOWS_UWP
|
#if WINDOWS_UWP
|
||||||
|
using Windows.Foundation;
|
||||||
using Windows.UI.Xaml;
|
using Windows.UI.Xaml;
|
||||||
using Windows.UI.Xaml.Media;
|
using Windows.UI.Xaml.Media;
|
||||||
using Windows.UI.Xaml.Media.Animation;
|
using Windows.UI.Xaml.Media.Animation;
|
||||||
|
|
@ -22,7 +23,8 @@ namespace MapControl
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 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 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.
|
/// 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.
|
/// 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(
|
public static readonly DependencyProperty MapProjectionProperty = DependencyProperty.Register(
|
||||||
nameof(MapProjection), typeof(MapProjection), typeof(MapBase),
|
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(
|
public static readonly DependencyProperty ProjectionCenterProperty = DependencyProperty.Register(
|
||||||
nameof(ProjectionCenter), typeof(Location), typeof(MapBase),
|
nameof(ProjectionCenter), typeof(Location), typeof(MapBase),
|
||||||
|
|
@ -224,6 +226,11 @@ namespace MapControl
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the transformation from cartesian map coordinates to viewport coordinates.
|
/// Gets the transformation from cartesian map coordinates to viewport coordinates.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
public ViewTransform ViewTransform { get; } = new ViewTransform();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the transformation from cartesian map coordinates to viewport coordinates as MatrixTransform.
|
||||||
|
/// </summary>
|
||||||
public MatrixTransform ViewportTransform { get; } = new MatrixTransform();
|
public MatrixTransform ViewportTransform { get; } = new MatrixTransform();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -239,14 +246,23 @@ namespace MapControl
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the combination of ScaleTransform and RotateTransform
|
/// Gets the combination of ScaleTransform and RotateTransform
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public TransformGroup ScaleRotateTransform { get; } = new TransformGroup();
|
public TransformGroup ScaleRotateTransform
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var transform = new TransformGroup();
|
||||||
|
transform.Children.Add(ScaleTransform);
|
||||||
|
transform.Children.Add(RotateTransform);
|
||||||
|
return transform;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Transforms a Location in geographic coordinates to a Point in viewport coordinates.
|
/// Transforms a Location in geographic coordinates to a Point in viewport coordinates.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Point LocationToViewportPoint(Location location)
|
public Point LocationToViewportPoint(Location location)
|
||||||
{
|
{
|
||||||
return MapProjection.LocationToViewportPoint(location);
|
return ViewTransform.MapToView(MapProjection.LocationToMap(location));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -254,7 +270,25 @@ namespace MapControl
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Location ViewportPointToLocation(Point point)
|
public Location ViewportPointToLocation(Point point)
|
||||||
{
|
{
|
||||||
return MapProjection.ViewportPointToLocation(point);
|
return MapProjection.MapToLocation(ViewTransform.ViewToMap(point));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Transforms a Rect in viewport coordinates to a BoundingBox in geographic coordinates.
|
||||||
|
/// </summary>
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -263,7 +297,7 @@ namespace MapControl
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void SetTransformCenter(Point center)
|
public void SetTransformCenter(Point center)
|
||||||
{
|
{
|
||||||
transformCenter = MapProjection.ViewportPointToLocation(center);
|
transformCenter = ViewportPointToLocation(center);
|
||||||
viewportCenter = center;
|
viewportCenter = center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -289,7 +323,7 @@ namespace MapControl
|
||||||
|
|
||||||
if (translation.X != 0d || translation.Y != 0d)
|
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)
|
if (rotation != 0d || scale != 1d)
|
||||||
{
|
{
|
||||||
transformCenter = MapProjection.ViewportPointToLocation(center);
|
transformCenter = ViewportPointToLocation(center);
|
||||||
viewportCenter = center + translation;
|
viewportCenter = center + translation;
|
||||||
|
|
||||||
if (rotation != 0d)
|
if (rotation != 0d)
|
||||||
|
|
@ -351,10 +385,10 @@ namespace MapControl
|
||||||
var rect = MapProjection.BoundingBoxToRect(boundingBox);
|
var rect = MapProjection.BoundingBoxToRect(boundingBox);
|
||||||
var center = new Point(rect.X + rect.Width / 2d, rect.Y + rect.Height / 2d);
|
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)
|
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);
|
TargetZoomLevel = Math.Log(scale, 2d);
|
||||||
TargetCenter = MapProjection.PointToLocation(center);
|
TargetCenter = MapProjection.MapToLocation(center);
|
||||||
TargetHeading = 0d;
|
TargetHeading = 0d;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -669,13 +703,16 @@ namespace MapControl
|
||||||
private void UpdateTransform(bool resetTransformCenter = false, bool projectionChanged = false)
|
private void UpdateTransform(bool resetTransformCenter = false, bool projectionChanged = false)
|
||||||
{
|
{
|
||||||
var projection = MapProjection;
|
var projection = MapProjection;
|
||||||
|
var viewportScale = 256d * Math.Pow(2d, ZoomLevel) / (360d * MapProjection.Wgs84MetersPerDegree);
|
||||||
var center = transformCenter ?? Center;
|
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)
|
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);
|
center.Longitude = Location.NormalizeLongitude(center.Longitude);
|
||||||
|
|
||||||
if (center.Latitude < -projection.MaxLatitude || center.Latitude > projection.MaxLatitude)
|
if (center.Latitude < -projection.MaxLatitude || center.Latitude > projection.MaxLatitude)
|
||||||
|
|
@ -694,17 +731,20 @@ namespace MapControl
|
||||||
if (resetTransformCenter)
|
if (resetTransformCenter)
|
||||||
{
|
{
|
||||||
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);
|
var scale = projection.GetRelativeScale(center);
|
||||||
ScaleTransform.ScaleX = scale.X;
|
ScaleTransform.ScaleX = scale.X * ViewTransform.Scale;
|
||||||
ScaleTransform.ScaleY = scale.Y;
|
ScaleTransform.ScaleY = scale.Y * ViewTransform.Scale;
|
||||||
|
|
||||||
RotateTransform.Angle = Heading;
|
RotateTransform.Angle = ViewTransform.Rotation;
|
||||||
|
|
||||||
OnViewportChanged(new ViewportChangedEventArgs(projectionChanged, Center.Longitude - centerLongitude));
|
OnViewportChanged(new ViewportChangedEventArgs(projectionChanged, Center.Longitude - centerLongitude));
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ namespace MapControl
|
||||||
|
|
||||||
private double GetLineDistance()
|
private double GetLineDistance()
|
||||||
{
|
{
|
||||||
var pixelPerDegree = ParentMap.MapProjection.ViewportScale * ParentMap.MapProjection.TrueScale;
|
var pixelPerDegree = ParentMap.ViewTransform.Scale * ParentMap.MapProjection.UnitsPerDegree;
|
||||||
var minDistance = MinLineDistance / pixelPerDegree;
|
var minDistance = MinLineDistance / pixelPerDegree;
|
||||||
var scale = 1d;
|
var scale = 1d;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -258,7 +258,7 @@ namespace MapControl
|
||||||
var y = (ParentMap.RenderSize.Height - height) / 2d;
|
var y = (ParentMap.RenderSize.Height - height) / 2d;
|
||||||
var rect = new Rect(x, y, width, height);
|
var rect = new Rect(x, y, width, height);
|
||||||
|
|
||||||
BoundingBox = ParentMap.MapProjection.ViewportRectToBoundingBox(rect);
|
BoundingBox = ParentMap.ViewportRectToBoundingBox(rect);
|
||||||
|
|
||||||
if (BoundingBox != null)
|
if (BoundingBox != null)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -145,14 +145,13 @@ namespace MapControl
|
||||||
|
|
||||||
private Point ArrangeElement(FrameworkElement element, Location location)
|
private Point ArrangeElement(FrameworkElement element, Location location)
|
||||||
{
|
{
|
||||||
var projection = parentMap.MapProjection;
|
var pos = parentMap.LocationToViewportPoint(location);
|
||||||
var pos = projection.LocationToViewportPoint(location);
|
|
||||||
|
|
||||||
if (projection.IsNormalCylindrical &&
|
if (parentMap.MapProjection.IsNormalCylindrical &&
|
||||||
(pos.X < 0d || pos.X > parentMap.RenderSize.Width ||
|
(pos.X < 0d || pos.X > parentMap.RenderSize.Width ||
|
||||||
pos.Y < 0d || pos.Y > parentMap.RenderSize.Height))
|
pos.Y < 0d || pos.Y > parentMap.RenderSize.Height))
|
||||||
{
|
{
|
||||||
pos = projection.LocationToViewportPoint(new Location(
|
pos = parentMap.LocationToViewportPoint(new Location(
|
||||||
location.Latitude,
|
location.Latitude,
|
||||||
Location.NearestLongitude(location.Longitude, parentMap.Center.Longitude)));
|
Location.NearestLongitude(location.Longitude, parentMap.Center.Longitude)));
|
||||||
}
|
}
|
||||||
|
|
@ -202,20 +201,20 @@ namespace MapControl
|
||||||
var projection = parentMap.MapProjection;
|
var projection = parentMap.MapProjection;
|
||||||
var rect = projection.BoundingBoxToRect(boundingBox);
|
var rect = projection.BoundingBoxToRect(boundingBox);
|
||||||
var center = new Point(rect.X + rect.Width / 2d, rect.Y + rect.Height / 2d);
|
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 &&
|
if (projection.IsNormalCylindrical &&
|
||||||
(pos.X < 0d || pos.X > parentMap.RenderSize.Width ||
|
(pos.X < 0d || pos.X > parentMap.RenderSize.Width ||
|
||||||
pos.Y < 0d || pos.Y > parentMap.RenderSize.Height))
|
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);
|
location.Longitude = Location.NearestLongitude(location.Longitude, parentMap.Center.Longitude);
|
||||||
|
|
||||||
pos = projection.LocationToViewportPoint(location);
|
pos = parentMap.LocationToViewportPoint(location);
|
||||||
}
|
}
|
||||||
|
|
||||||
rect.Width *= projection.ViewportScale;
|
rect.Width *= parentMap.ViewTransform.Scale;
|
||||||
rect.Height *= projection.ViewportScale;
|
rect.Height *= parentMap.ViewTransform.Scale;
|
||||||
rect.X = pos.X - rect.Width / 2d;
|
rect.X = pos.X - rect.Width / 2d;
|
||||||
rect.Y = pos.Y - rect.Height / 2d;
|
rect.Y = pos.Y - rect.Height / 2d;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,24 +6,26 @@ using System;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
#if WINDOWS_UWP
|
#if WINDOWS_UWP
|
||||||
using Windows.Foundation;
|
using Windows.Foundation;
|
||||||
using Windows.UI.Xaml.Media;
|
|
||||||
#else
|
#else
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Media;
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace MapControl
|
namespace MapControl
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Defines a map projection between geographic coordinates, cartesian map coordinates and viewport coordinates.
|
/// Defines a map projection between geographic coordinates and cartesian map coordinates.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract class MapProjection
|
public abstract class MapProjection
|
||||||
{
|
{
|
||||||
public const double Wgs84EquatorialRadius = 6378137d;
|
public const double Wgs84EquatorialRadius = 6378137d;
|
||||||
|
public const double Wgs84MetersPerDegree = Wgs84EquatorialRadius * Math.PI / 180d;
|
||||||
public const double Wgs84Flattening = 1d / 298.257223563;
|
public const double Wgs84Flattening = 1d / 298.257223563;
|
||||||
public static readonly double Wgs84Eccentricity = Math.Sqrt((2d - Wgs84Flattening) * Wgs84Flattening);
|
public static readonly double Wgs84Eccentricity = Math.Sqrt((2d - Wgs84Flattening) * Wgs84Flattening);
|
||||||
|
|
||||||
public const double Wgs84MetersPerDegree = Wgs84EquatorialRadius * Math.PI / 180d;
|
/// <summary>
|
||||||
|
/// Gets or sets the projection center. Only relevant for azimuthal projections.
|
||||||
|
/// </summary>
|
||||||
|
public Location Center { get; set; } = new Location();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the WMS 1.3.0 CRS identifier.
|
/// 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
|
/// 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.
|
/// cylindrical projection (usually the equator), or at the projection center of an azimuthal projection.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public virtual double TrueScale
|
public virtual double UnitsPerDegree
|
||||||
{
|
{
|
||||||
get { return Wgs84MetersPerDegree; }
|
get { return Wgs84MetersPerDegree; }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the projection center. Only relevant for azimuthal projections.
|
/// Gets the relative map scale at the specified Location.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Location ProjectionCenter { get; private set; } = new Location();
|
public virtual Vector GetRelativeScale(Location location)
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the transform matrix from cartesian map coordinates to viewport coordinates.
|
|
||||||
/// </summary>
|
|
||||||
public Matrix ViewportTransform { get; private set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the transform matrix from viewport coordinates to cartesian map coordinates.
|
|
||||||
/// </summary>
|
|
||||||
public Matrix InverseViewportTransform { get; private set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the rotation angle of the ViewportTransform matrix.
|
|
||||||
/// </summary>
|
|
||||||
public double ViewportRotation { get; private set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the scaling factor from cartesian map coordinates to viewport coordinates
|
|
||||||
/// at the projection's point of true scale.
|
|
||||||
/// </summary>
|
|
||||||
public double ViewportScale { get; private set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the map scale at the specified Location as viewport coordinate units per meter (px/m).
|
|
||||||
/// </summary>
|
|
||||||
public virtual Vector GetMapScale(Location location)
|
|
||||||
{
|
{
|
||||||
return new Vector(ViewportScale, ViewportScale);
|
return new Vector(1, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Transforms a Location in geographic coordinates to a Point in cartesian map coordinates.
|
/// Transforms a Location in geographic coordinates to a Point in cartesian map coordinates.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract Point LocationToPoint(Location location);
|
public abstract Point LocationToMap(Location location);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Transforms a Point in cartesian map coordinates to a Location in geographic coordinates.
|
/// Transforms a Point in cartesian map coordinates to a Location in geographic coordinates.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract Location PointToLocation(Point point);
|
public abstract Location MapToLocation(Point point);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Transforms a BoundingBox in geographic coordinates to a Rect in cartesian map coordinates.
|
/// 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)
|
public virtual Rect BoundingBoxToRect(BoundingBox boundingBox)
|
||||||
{
|
{
|
||||||
return new Rect(
|
return new Rect(
|
||||||
LocationToPoint(new Location(boundingBox.South, boundingBox.West)),
|
LocationToMap(new Location(boundingBox.South, boundingBox.West)),
|
||||||
LocationToPoint(new Location(boundingBox.North, boundingBox.East)));
|
LocationToMap(new Location(boundingBox.North, boundingBox.East)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -122,53 +98,19 @@ namespace MapControl
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public virtual BoundingBox RectToBoundingBox(Rect rect)
|
public virtual BoundingBox RectToBoundingBox(Rect rect)
|
||||||
{
|
{
|
||||||
var sw = PointToLocation(new Point(rect.X, rect.Y));
|
var sw = MapToLocation(new Point(rect.X, rect.Y));
|
||||||
var ne = PointToLocation(new Point(rect.X + rect.Width, rect.Y + rect.Height));
|
var ne = MapToLocation(new Point(rect.X + rect.Width, rect.Y + rect.Height));
|
||||||
|
|
||||||
return new BoundingBox(sw.Latitude, sw.Longitude, ne.Latitude, ne.Longitude);
|
return new BoundingBox(sw.Latitude, sw.Longitude, ne.Latitude, ne.Longitude);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Transforms a Location in geographic coordinates to a Point in viewport coordinates.
|
|
||||||
/// </summary>
|
|
||||||
public Point LocationToViewportPoint(Location location)
|
|
||||||
{
|
|
||||||
return ViewportTransform.Transform(LocationToPoint(location));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Transforms a Point in viewport coordinates to a Location in geographic coordinates.
|
|
||||||
/// </summary>
|
|
||||||
public Location ViewportPointToLocation(Point point)
|
|
||||||
{
|
|
||||||
return PointToLocation(InverseViewportTransform.Transform(point));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Transforms a Rect in viewport coordinates to a BoundingBox in geographic coordinates.
|
|
||||||
/// </summary>
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the CRS parameter value for a WMS GetMap request.
|
/// Gets the CRS parameter value for a WMS GetMap request.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public virtual string GetCrsValue()
|
public virtual string GetCrsValue()
|
||||||
{
|
{
|
||||||
return CrsId.StartsWith("AUTO:") || CrsId.StartsWith("AUTO2:")
|
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;
|
: CrsId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -180,78 +122,5 @@ namespace MapControl
|
||||||
return string.Format(CultureInfo.InvariantCulture,
|
return string.Format(CultureInfo.InvariantCulture,
|
||||||
"{0},{1},{2},{3}", rect.X, rect.Y, (rect.X + rect.Width), (rect.Y + rect.Height));
|
"{0},{1},{2},{3}", rect.X, rect.Y, (rect.X + rect.Width), (rect.Y + rect.Height));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Sets ProjectionCenter, ViewportScale, ViewportRotation, ViewportTransform and InverseViewportTransform.
|
|
||||||
/// </summary>
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -81,9 +81,9 @@ namespace MapControl
|
||||||
MapPanel.InitMapElement(this);
|
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)
|
if (point.Y == double.PositiveInfinity)
|
||||||
{
|
{
|
||||||
|
|
@ -99,7 +99,7 @@ namespace MapControl
|
||||||
|
|
||||||
protected Point LocationToViewportPoint(Location location)
|
protected Point LocationToViewportPoint(Location location)
|
||||||
{
|
{
|
||||||
return parentMap.MapProjection.ViewportTransform.Transform(LocationToPoint(location));
|
return parentMap.ViewTransform.MapToView(LocationToMap(location));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected double GetLongitudeOffset()
|
protected double GetLongitudeOffset()
|
||||||
|
|
|
||||||
|
|
@ -23,10 +23,10 @@ namespace MapControl
|
||||||
{
|
{
|
||||||
public const int TileSize = 256;
|
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);
|
-180d * MapProjection.Wgs84MetersPerDegree, 180d * MapProjection.Wgs84MetersPerDegree);
|
||||||
|
|
||||||
public static double TileGridScale(int zoomLevel)
|
public static double TileMatrixScale(int zoomLevel)
|
||||||
{
|
{
|
||||||
return (TileSize << zoomLevel) / (360d * MapProjection.Wgs84MetersPerDegree);
|
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<Tile> Tiles { get; private set; } = new List<Tile>();
|
public IReadOnlyCollection<Tile> Tiles { get; private set; } = new List<Tile>();
|
||||||
|
|
||||||
|
|
@ -88,7 +88,7 @@ namespace MapControl
|
||||||
|
|
||||||
protected override void TileSourcePropertyChanged()
|
protected override void TileSourcePropertyChanged()
|
||||||
{
|
{
|
||||||
if (TileGrid != null)
|
if (TileMatrix != null)
|
||||||
{
|
{
|
||||||
Tiles = new List<Tile>();
|
Tiles = new List<Tile>();
|
||||||
UpdateTiles();
|
UpdateTiles();
|
||||||
|
|
@ -101,10 +101,10 @@ namespace MapControl
|
||||||
|
|
||||||
if (ParentMap == null || !ParentMap.MapProjection.IsWebMercator)
|
if (ParentMap == null || !ParentMap.MapProjection.IsWebMercator)
|
||||||
{
|
{
|
||||||
TileGrid = null;
|
TileMatrix = null;
|
||||||
UpdateTiles();
|
UpdateTiles();
|
||||||
}
|
}
|
||||||
else if (SetTileGrid())
|
else if (SetTileMatrix())
|
||||||
{
|
{
|
||||||
SetRenderTransform();
|
SetRenderTransform();
|
||||||
UpdateTiles();
|
UpdateTiles();
|
||||||
|
|
@ -113,22 +113,22 @@ namespace MapControl
|
||||||
|
|
||||||
protected override void SetRenderTransform()
|
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(
|
((MatrixTransform)RenderTransform).Matrix = ParentMap.ViewTransform.GetTileLayerTransform(
|
||||||
TileGridScale(TileGrid.ZoomLevel), TileGridTopLeft, tileGridOrigin);
|
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
|
// bounds in tile pixels from viewport size
|
||||||
//
|
//
|
||||||
var tileBounds = ParentMap.MapProjection.GetTileBounds(
|
var tileBounds = ParentMap.ViewTransform.GetTileMatrixBounds(
|
||||||
TileGridScale(tileGridZoomLevel), TileGridTopLeft, ParentMap.RenderSize);
|
TileMatrixScale(tileMatrixZoomLevel), TileMatrixTopLeft, ParentMap.RenderSize);
|
||||||
|
|
||||||
// tile column and row index bounds
|
// tile column and row index bounds
|
||||||
//
|
//
|
||||||
|
|
@ -137,15 +137,15 @@ namespace MapControl
|
||||||
var xMax = (int)Math.Floor((tileBounds.X + tileBounds.Width) / TileSize);
|
var xMax = (int)Math.Floor((tileBounds.X + tileBounds.Width) / TileSize);
|
||||||
var yMax = (int)Math.Floor((tileBounds.Y + tileBounds.Height) / TileSize);
|
var yMax = (int)Math.Floor((tileBounds.Y + tileBounds.Height) / TileSize);
|
||||||
|
|
||||||
if (TileGrid != null &&
|
if (TileMatrix != null &&
|
||||||
TileGrid.ZoomLevel == tileGridZoomLevel &&
|
TileMatrix.ZoomLevel == tileMatrixZoomLevel &&
|
||||||
TileGrid.XMin == xMin && TileGrid.YMin == yMin &&
|
TileMatrix.XMin == xMin && TileMatrix.YMin == yMin &&
|
||||||
TileGrid.XMax == xMax && TileGrid.YMax == yMax)
|
TileMatrix.XMax == xMax && TileMatrix.YMax == yMax)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
TileGrid = new TileGrid(tileGridZoomLevel, xMin, yMin, xMax, yMax);
|
TileMatrix = new TileMatrix(tileMatrixZoomLevel, xMin, yMin, xMax, yMax);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -154,9 +154,9 @@ namespace MapControl
|
||||||
{
|
{
|
||||||
var newTiles = new List<Tile>();
|
var newTiles = new List<Tile>();
|
||||||
|
|
||||||
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)
|
if (maxZoomLevel >= MinZoomLevel)
|
||||||
{
|
{
|
||||||
|
|
@ -164,16 +164,16 @@ namespace MapControl
|
||||||
|
|
||||||
if (this == ParentMap.MapLayer) // load background tiles
|
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++)
|
for (var z = minZoomLevel; z <= maxZoomLevel; z++)
|
||||||
{
|
{
|
||||||
var tileSize = 1 << (TileGrid.ZoomLevel - z);
|
var tileSize = 1 << (TileMatrix.ZoomLevel - z);
|
||||||
var x1 = (int)Math.Floor((double)TileGrid.XMin / tileSize); // may be negative
|
var x1 = (int)Math.Floor((double)TileMatrix.XMin / tileSize); // may be negative
|
||||||
var x2 = TileGrid.XMax / tileSize;
|
var x2 = TileMatrix.XMax / tileSize;
|
||||||
var y1 = Math.Max(TileGrid.YMin / tileSize, 0);
|
var y1 = Math.Max(TileMatrix.YMin / tileSize, 0);
|
||||||
var y2 = Math.Min(TileGrid.YMax / tileSize, (1 << z) - 1);
|
var y2 = Math.Min(TileMatrix.YMax / tileSize, (1 << z) - 1);
|
||||||
|
|
||||||
for (var y = y1; y <= y2; y++)
|
for (var y = y1; y <= y2; y++)
|
||||||
{
|
{
|
||||||
|
|
@ -227,13 +227,13 @@ namespace MapControl
|
||||||
|
|
||||||
protected override Size ArrangeOverride(Size finalSize)
|
protected override Size ArrangeOverride(Size finalSize)
|
||||||
{
|
{
|
||||||
if (TileGrid != null)
|
if (TileMatrix != null)
|
||||||
{
|
{
|
||||||
foreach (var tile in Tiles)
|
foreach (var tile in Tiles)
|
||||||
{
|
{
|
||||||
var tileSize = TileSize << (TileGrid.ZoomLevel - tile.ZoomLevel);
|
var tileSize = TileSize << (TileMatrix.ZoomLevel - tile.ZoomLevel);
|
||||||
var x = tileSize * tile.X - TileSize * TileGrid.XMin;
|
var x = tileSize * tile.X - TileSize * TileMatrix.XMin;
|
||||||
var y = tileSize * tile.Y - TileSize * TileGrid.YMin;
|
var y = tileSize * tile.Y - TileSize * TileMatrix.YMin;
|
||||||
|
|
||||||
tile.Image.Width = tileSize;
|
tile.Image.Width = tileSize;
|
||||||
tile.Image.Height = tileSize;
|
tile.Image.Height = tileSize;
|
||||||
|
|
|
||||||
|
|
@ -19,31 +19,31 @@ namespace MapControl
|
||||||
CrsId = "AUTO2:42003";
|
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();
|
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 lat = location.Latitude * Math.PI / 180d;
|
||||||
var dLon = (location.Longitude - ProjectionCenter.Longitude) * Math.PI / 180d;
|
var dLon = (location.Longitude - Center.Longitude) * Math.PI / 180d;
|
||||||
var s = TrueScale * 180d / Math.PI;
|
var s = UnitsPerDegree * 180d / Math.PI;
|
||||||
|
|
||||||
return new Point(
|
return new Point(
|
||||||
s * Math.Cos(lat) * Math.Sin(dLon),
|
s * Math.Cos(lat) * Math.Sin(dLon),
|
||||||
s * (Math.Cos(lat0) * Math.Sin(lat) - Math.Sin(lat0) * Math.Cos(lat) * Math.Cos(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)
|
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 x = point.X / s;
|
||||||
var y = point.Y / s;
|
var y = point.Y / s;
|
||||||
var r2 = x * x + y * y;
|
var r2 = x * x + y * y;
|
||||||
|
|
@ -57,13 +57,13 @@ namespace MapControl
|
||||||
var sinC = r;
|
var sinC = r;
|
||||||
var cosC = Math.Sqrt(1 - r2);
|
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 cosLat0 = Math.Cos(lat0);
|
||||||
var sinLat0 = Math.Sin(lat0);
|
var sinLat0 = Math.Sin(lat0);
|
||||||
|
|
||||||
return new Location(
|
return new Location(
|
||||||
180d / Math.PI * Math.Asin(cosC * sinLat0 + y * sinC * cosLat0 / r),
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,35 +19,35 @@ namespace MapControl
|
||||||
CrsId = "AUTO2:97002"; // GeoServer non-standard CRS ID
|
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();
|
return new Point();
|
||||||
}
|
}
|
||||||
|
|
||||||
double azimuth, distance;
|
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));
|
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)
|
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 azimuth = Math.Atan2(point.X, point.Y);
|
||||||
var mapDistance = Math.Sqrt(point.X * point.X + point.Y * 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
namespace MapControl
|
namespace MapControl
|
||||||
{
|
{
|
||||||
public class TileGrid
|
public class TileMatrix
|
||||||
{
|
{
|
||||||
public readonly int ZoomLevel;
|
public readonly int ZoomLevel;
|
||||||
public readonly int XMin;
|
public readonly int XMin;
|
||||||
|
|
@ -12,7 +12,7 @@ namespace MapControl
|
||||||
public readonly int XMax;
|
public readonly int XMax;
|
||||||
public readonly int YMax;
|
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;
|
ZoomLevel = zoomLevel;
|
||||||
XMin = xMin;
|
XMin = xMin;
|
||||||
118
MapControl/Shared/ViewTransform.cs
Normal file
118
MapControl/Shared/ViewTransform.cs
Normal file
|
|
@ -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
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Defines the transformation between cartesian map coordinates and viewport coordinates.
|
||||||
|
/// </summary>
|
||||||
|
public class ViewTransform
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the transform matrix from cartesian map coordinates to viewport coordinates.
|
||||||
|
/// </summary>
|
||||||
|
public Matrix MapToViewMatrix { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the transform matrix from viewport coordinates to cartesian map coordinates.
|
||||||
|
/// </summary>
|
||||||
|
public Matrix ViewToMapMatrix { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the scaling factor from cartesian map coordinates to viewport coordinates.
|
||||||
|
/// </summary>
|
||||||
|
public double Scale { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the rotation angle of the transform matrix.
|
||||||
|
/// </summary>
|
||||||
|
public double Rotation { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Transforms a Point from cartesian map coordinates to viewport coordinates.
|
||||||
|
/// </summary>
|
||||||
|
public Point MapToView(Point point)
|
||||||
|
{
|
||||||
|
return MapToViewMatrix.Transform(point);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Transforms a Point from viewport coordinates to cartesian map coordinates.
|
||||||
|
/// </summary>
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -32,25 +32,25 @@ namespace MapControl
|
||||||
get { return maxLatitude; }
|
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)
|
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(
|
return new Point(
|
||||||
TrueScale * location.Longitude,
|
UnitsPerDegree * location.Longitude,
|
||||||
TrueScale * LatitudeToY(location.Latitude));
|
UnitsPerDegree * LatitudeToY(location.Latitude));
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Location PointToLocation(Point point)
|
public override Location MapToLocation(Point point)
|
||||||
{
|
{
|
||||||
return new Location(
|
return new Location(
|
||||||
YToLatitude(point.Y / TrueScale),
|
YToLatitude(point.Y / UnitsPerDegree),
|
||||||
point.X / TrueScale);
|
point.X / UnitsPerDegree);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static double LatitudeToY(double latitude)
|
public static double LatitudeToY(double latitude)
|
||||||
|
|
|
||||||
|
|
@ -142,8 +142,8 @@ namespace MapControl
|
||||||
|
|
||||||
uri += "&CRS=" + projection.GetCrsValue();
|
uri += "&CRS=" + projection.GetCrsValue();
|
||||||
uri += "&BBOX=" + projection.GetBboxValue(rect);
|
uri += "&BBOX=" + projection.GetBboxValue(rect);
|
||||||
uri += "&WIDTH=" + (int)Math.Round(projection.ViewportScale * rect.Width);
|
uri += "&WIDTH=" + (int)Math.Round(ParentMap.ViewTransform.Scale * rect.Width);
|
||||||
uri += "&HEIGHT=" + (int)Math.Round(projection.ViewportScale * rect.Height);
|
uri += "&HEIGHT=" + (int)Math.Round(ParentMap.ViewTransform.Scale * rect.Height);
|
||||||
}
|
}
|
||||||
|
|
||||||
return uri;
|
return uri;
|
||||||
|
|
|
||||||
|
|
@ -104,14 +104,14 @@ namespace MapControl
|
||||||
{
|
{
|
||||||
foreach (var layer in ChildLayers)
|
foreach (var layer in ChildLayers)
|
||||||
{
|
{
|
||||||
layer.SetRenderTransform(ParentMap.MapProjection);
|
layer.SetRenderTransform(ParentMap.ViewTransform);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool UpdateChildLayers(WmtsTileMatrixSet tileMatrixSet)
|
private bool UpdateChildLayers(WmtsTileMatrixSet tileMatrixSet)
|
||||||
{
|
{
|
||||||
var layersChanged = false;
|
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
|
// show all TileMatrix layers with Scale <= maxScale, at least the first layer
|
||||||
//
|
//
|
||||||
|
|
@ -142,7 +142,7 @@ namespace MapControl
|
||||||
layersChanged = true;
|
layersChanged = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (layer.SetBounds(ParentMap.MapProjection, ParentMap.RenderSize))
|
if (layer.SetBounds(ParentMap.ViewTransform, ParentMap.RenderSize))
|
||||||
{
|
{
|
||||||
layersChanged = true;
|
layersChanged = true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -37,21 +37,21 @@ namespace MapControl
|
||||||
|
|
||||||
public IReadOnlyCollection<Tile> Tiles { get; private set; } = new List<Tile>();
|
public IReadOnlyCollection<Tile> Tiles { get; private set; } = new List<Tile>();
|
||||||
|
|
||||||
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 =
|
((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
|
// 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
|
// tile column and row index bounds
|
||||||
//
|
//
|
||||||
|
|
|
||||||
|
|
@ -30,27 +30,27 @@ namespace MapControl
|
||||||
get { return maxLatitude; }
|
get { return maxLatitude; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Vector GetMapScale(Location location)
|
public override Vector GetRelativeScale(Location location)
|
||||||
{
|
{
|
||||||
var lat = location.Latitude * Math.PI / 180d;
|
var lat = location.Latitude * Math.PI / 180d;
|
||||||
var eSinLat = Wgs84Eccentricity * Math.Sin(lat);
|
var eSinLat = Wgs84Eccentricity * Math.Sin(lat);
|
||||||
var k = Math.Sqrt(1d - eSinLat * eSinLat) / Math.Cos(lat); // p.44 (7-8)
|
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(
|
return new Point(
|
||||||
TrueScale * location.Longitude,
|
UnitsPerDegree * location.Longitude,
|
||||||
TrueScale * LatitudeToY(location.Latitude));
|
UnitsPerDegree * LatitudeToY(location.Latitude));
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Location PointToLocation(Point point)
|
public override Location MapToLocation(Point point)
|
||||||
{
|
{
|
||||||
return new Location(
|
return new Location(
|
||||||
YToLatitude(point.Y / TrueScale),
|
YToLatitude(point.Y / UnitsPerDegree),
|
||||||
point.X / TrueScale);
|
point.X / UnitsPerDegree);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static double LatitudeToY(double latitude)
|
public static double LatitudeToY(double latitude)
|
||||||
|
|
|
||||||
|
|
@ -45,10 +45,6 @@ namespace MapControl
|
||||||
|
|
||||||
public MapBase()
|
public MapBase()
|
||||||
{
|
{
|
||||||
MapProjection = new WebMercatorProjection();
|
|
||||||
ScaleRotateTransform.Children.Add(ScaleTransform);
|
|
||||||
ScaleRotateTransform.Children.Add(RotateTransform);
|
|
||||||
|
|
||||||
// set Background by Style to enable resetting by ClearValue in MapLayerPropertyChanged
|
// set Background by Style to enable resetting by ClearValue in MapLayerPropertyChanged
|
||||||
var style = new Style(typeof(MapBase));
|
var style = new Style(typeof(MapBase));
|
||||||
style.Setters.Add(new Setter(BackgroundProperty, new SolidColorBrush(Colors.Transparent)));
|
style.Setters.Add(new Setter(BackgroundProperty, new SolidColorBrush(Colors.Transparent)));
|
||||||
|
|
|
||||||
|
|
@ -140,18 +140,21 @@
|
||||||
<Compile Include="..\Shared\Tile.cs">
|
<Compile Include="..\Shared\Tile.cs">
|
||||||
<Link>Tile.cs</Link>
|
<Link>Tile.cs</Link>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="..\Shared\TileGrid.cs">
|
|
||||||
<Link>TileGrid.cs</Link>
|
|
||||||
</Compile>
|
|
||||||
<Compile Include="..\Shared\TileImageLoader.cs">
|
<Compile Include="..\Shared\TileImageLoader.cs">
|
||||||
<Link>TileImageLoader.cs</Link>
|
<Link>TileImageLoader.cs</Link>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="..\Shared\TileMatrix.cs">
|
||||||
|
<Link>TileMatrix.cs</Link>
|
||||||
|
</Compile>
|
||||||
<Compile Include="..\Shared\TileSource.cs">
|
<Compile Include="..\Shared\TileSource.cs">
|
||||||
<Link>TileSource.cs</Link>
|
<Link>TileSource.cs</Link>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="..\Shared\ViewportChangedEventArgs.cs">
|
<Compile Include="..\Shared\ViewportChangedEventArgs.cs">
|
||||||
<Link>ViewportChangedEventArgs.cs</Link>
|
<Link>ViewportChangedEventArgs.cs</Link>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="..\Shared\ViewTransform.cs">
|
||||||
|
<Link>ViewTransform.cs</Link>
|
||||||
|
</Compile>
|
||||||
<Compile Include="..\Shared\WebMercatorProjection.cs">
|
<Compile Include="..\Shared\WebMercatorProjection.cs">
|
||||||
<Link>WebMercatorProjection.cs</Link>
|
<Link>WebMercatorProjection.cs</Link>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,8 @@ namespace MapControl
|
||||||
|
|
||||||
protected override void OnViewportChanged(ViewportChangedEventArgs e)
|
protected override void OnViewportChanged(ViewportChangedEventArgs e)
|
||||||
{
|
{
|
||||||
var projection = ParentMap.MapProjection;
|
var map = ParentMap;
|
||||||
|
var projection = map.MapProjection;
|
||||||
|
|
||||||
if (projection.IsNormalCylindrical)
|
if (projection.IsNormalCylindrical)
|
||||||
{
|
{
|
||||||
|
|
@ -39,7 +40,7 @@ namespace MapControl
|
||||||
Children.Add(path);
|
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 lineDistance = GetLineDistance();
|
||||||
|
|
||||||
var labelStart = new Location(
|
var labelStart = new Location(
|
||||||
|
|
@ -65,14 +66,14 @@ namespace MapControl
|
||||||
{
|
{
|
||||||
var figure = new PathFigure
|
var figure = new PathFigure
|
||||||
{
|
{
|
||||||
StartPoint = projection.LocationToViewportPoint(new Location(lat, lineStart.Longitude)),
|
StartPoint = map.LocationToViewportPoint(new Location(lat, lineStart.Longitude)),
|
||||||
IsClosed = false,
|
IsClosed = false,
|
||||||
IsFilled = false
|
IsFilled = false
|
||||||
};
|
};
|
||||||
|
|
||||||
figure.Segments.Add(new LineSegment
|
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);
|
geometry.Figures.Add(figure);
|
||||||
|
|
@ -82,14 +83,14 @@ namespace MapControl
|
||||||
{
|
{
|
||||||
var figure = new PathFigure
|
var figure = new PathFigure
|
||||||
{
|
{
|
||||||
StartPoint = projection.LocationToViewportPoint(new Location(lineStart.Latitude, lon)),
|
StartPoint = map.LocationToViewportPoint(new Location(lineStart.Latitude, lon)),
|
||||||
IsClosed = false,
|
IsClosed = false,
|
||||||
IsFilled = false
|
IsFilled = false
|
||||||
};
|
};
|
||||||
|
|
||||||
figure.Segments.Add(new LineSegment
|
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);
|
geometry.Figures.Add(figure);
|
||||||
|
|
@ -112,7 +113,7 @@ namespace MapControl
|
||||||
{
|
{
|
||||||
var renderTransform = new TransformGroup();
|
var renderTransform = new TransformGroup();
|
||||||
renderTransform.Children.Add(new TranslateTransform());
|
renderTransform.Children.Add(new TranslateTransform());
|
||||||
renderTransform.Children.Add(ParentMap.RotateTransform);
|
renderTransform.Children.Add(map.RotateTransform);
|
||||||
renderTransform.Children.Add(new TranslateTransform());
|
renderTransform.Children.Add(new TranslateTransform());
|
||||||
|
|
||||||
label = new TextBlock { RenderTransform = renderTransform };
|
label = new TextBlock { RenderTransform = renderTransform };
|
||||||
|
|
@ -153,7 +154,7 @@ namespace MapControl
|
||||||
var label = (TextBlock)Children[i];
|
var label = (TextBlock)Children[i];
|
||||||
var location = (Location)label.Tag;
|
var location = (Location)label.Tag;
|
||||||
var viewportTransform = (TranslateTransform)((TransformGroup)label.RenderTransform).Children[2];
|
var viewportTransform = (TranslateTransform)((TransformGroup)label.RenderTransform).Children[2];
|
||||||
var viewportPosition = projection.LocationToViewportPoint(location);
|
var viewportPosition = map.LocationToViewportPoint(location);
|
||||||
viewportTransform.X = viewportPosition.X;
|
viewportTransform.X = viewportPosition.X;
|
||||||
viewportTransform.Y = viewportPosition.Y;
|
viewportTransform.Y = viewportPosition.Y;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,8 @@ using System.Runtime.InteropServices;
|
||||||
[assembly: AssemblyCompany("Clemens Fischer")]
|
[assembly: AssemblyCompany("Clemens Fischer")]
|
||||||
[assembly: AssemblyCopyright("Copyright © 2020 Clemens Fischer")]
|
[assembly: AssemblyCopyright("Copyright © 2020 Clemens Fischer")]
|
||||||
[assembly: AssemblyTrademark("")]
|
[assembly: AssemblyTrademark("")]
|
||||||
[assembly: AssemblyVersion("4.17.0")]
|
[assembly: AssemblyVersion("5.0.0")]
|
||||||
[assembly: AssemblyFileVersion("4.17.0")]
|
[assembly: AssemblyFileVersion("5.0.0")]
|
||||||
[assembly: AssemblyConfiguration("")]
|
[assembly: AssemblyConfiguration("")]
|
||||||
[assembly: AssemblyCulture("")]
|
[assembly: AssemblyCulture("")]
|
||||||
[assembly: ComVisible(false)]
|
[assembly: ComVisible(false)]
|
||||||
|
|
|
||||||
|
|
@ -53,13 +53,6 @@ namespace MapControl
|
||||||
BackgroundProperty.OverrideMetadata(typeof(MapBase), new FrameworkPropertyMetadata(Brushes.Transparent));
|
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)
|
protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
|
||||||
{
|
{
|
||||||
base.OnRenderSizeChanged(sizeInfo);
|
base.OnRenderSizeChanged(sizeInfo);
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
<AssemblyOriginatorKeyFile>..\..\MapControl.snk</AssemblyOriginatorKeyFile>
|
<AssemblyOriginatorKeyFile>..\..\MapControl.snk</AssemblyOriginatorKeyFile>
|
||||||
<DelaySign>false</DelaySign>
|
<DelaySign>false</DelaySign>
|
||||||
<Product>XAML Map Control</Product>
|
<Product>XAML Map Control</Product>
|
||||||
<Version>4.17.0</Version>
|
<Version>5.0.0</Version>
|
||||||
<Description>XAML Map Control Library</Description>
|
<Description>XAML Map Control Library</Description>
|
||||||
<Authors>Clemens Fischer</Authors>
|
<Authors>Clemens Fischer</Authors>
|
||||||
<Copyright>Copyright © 2020 Clemens Fischer</Copyright>
|
<Copyright>Copyright © 2020 Clemens Fischer</Copyright>
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,7 @@ namespace MapControl
|
||||||
|
|
||||||
if (projection.IsNormalCylindrical)
|
if (projection.IsNormalCylindrical)
|
||||||
{
|
{
|
||||||
DrawCylindricalGraticule(drawingContext, projection, lineDistance, labelFormat);
|
DrawCylindricalGraticule(drawingContext, lineDistance, labelFormat);
|
||||||
}
|
}
|
||||||
else
|
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 latLabelStart = Math.Ceiling(boundingBox.South / lineDistance) * lineDistance;
|
||||||
var lonLabelStart = Math.Ceiling(boundingBox.West / lineDistance) * lineDistance;
|
var lonLabelStart = Math.Ceiling(boundingBox.West / lineDistance) * lineDistance;
|
||||||
var latLabels = new List<Label>((int)((boundingBox.North - latLabelStart) / lineDistance) + 1);
|
var latLabels = new List<Label>((int)((boundingBox.North - latLabelStart) / lineDistance) + 1);
|
||||||
|
|
@ -72,8 +72,8 @@ namespace MapControl
|
||||||
CultureInfo.InvariantCulture, FlowDirection.LeftToRight, typeface, FontSize, Foreground, pixelsPerDip)));
|
CultureInfo.InvariantCulture, FlowDirection.LeftToRight, typeface, FontSize, Foreground, pixelsPerDip)));
|
||||||
|
|
||||||
drawingContext.DrawLine(pen,
|
drawingContext.DrawLine(pen,
|
||||||
projection.LocationToViewportPoint(new Location(lat, boundingBox.West)),
|
ParentMap.LocationToViewportPoint(new Location(lat, boundingBox.West)),
|
||||||
projection.LocationToViewportPoint(new Location(lat, boundingBox.East)));
|
ParentMap.LocationToViewportPoint(new Location(lat, boundingBox.East)));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var lon = lonLabelStart; lon <= boundingBox.East; lon += lineDistance)
|
for (var lon = lonLabelStart; lon <= boundingBox.East; lon += lineDistance)
|
||||||
|
|
@ -83,15 +83,15 @@ namespace MapControl
|
||||||
CultureInfo.InvariantCulture, FlowDirection.LeftToRight, typeface, FontSize, Foreground, pixelsPerDip)));
|
CultureInfo.InvariantCulture, FlowDirection.LeftToRight, typeface, FontSize, Foreground, pixelsPerDip)));
|
||||||
|
|
||||||
drawingContext.DrawLine(pen,
|
drawingContext.DrawLine(pen,
|
||||||
projection.LocationToViewportPoint(new Location(boundingBox.South, lon)),
|
ParentMap.LocationToViewportPoint(new Location(boundingBox.South, lon)),
|
||||||
projection.LocationToViewportPoint(new Location(boundingBox.North, lon)));
|
ParentMap.LocationToViewportPoint(new Location(boundingBox.North, lon)));
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var latLabel in latLabels)
|
foreach (var latLabel in latLabels)
|
||||||
{
|
{
|
||||||
foreach (var lonLabel in lonLabels)
|
foreach (var lonLabel in lonLabels)
|
||||||
{
|
{
|
||||||
var position = projection.LocationToViewportPoint(new Location(latLabel.Position, lonLabel.Position));
|
var position = ParentMap.LocationToViewportPoint(new Location(latLabel.Position, lonLabel.Position));
|
||||||
|
|
||||||
drawingContext.PushTransform(new RotateTransform(ParentMap.Heading, position.X, position.Y));
|
drawingContext.PushTransform(new RotateTransform(ParentMap.Heading, position.X, position.Y));
|
||||||
drawingContext.DrawText(latLabel.Text,
|
drawingContext.DrawText(latLabel.Text,
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,8 @@ using System.Runtime.InteropServices;
|
||||||
[assembly: AssemblyCompany("Clemens Fischer")]
|
[assembly: AssemblyCompany("Clemens Fischer")]
|
||||||
[assembly: AssemblyCopyright("Copyright © 2020 Clemens Fischer")]
|
[assembly: AssemblyCopyright("Copyright © 2020 Clemens Fischer")]
|
||||||
[assembly: AssemblyTrademark("")]
|
[assembly: AssemblyTrademark("")]
|
||||||
[assembly: AssemblyVersion("4.17.0")]
|
[assembly: AssemblyVersion("5.0.0")]
|
||||||
[assembly: AssemblyFileVersion("4.17.0")]
|
[assembly: AssemblyFileVersion("5.0.0")]
|
||||||
[assembly: AssemblyConfiguration("")]
|
[assembly: AssemblyConfiguration("")]
|
||||||
[assembly: AssemblyCulture("")]
|
[assembly: AssemblyCulture("")]
|
||||||
[assembly: ComVisible(false)]
|
[assembly: ComVisible(false)]
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,8 @@ using System.Runtime.InteropServices;
|
||||||
[assembly: AssemblyCompany("Clemens Fischer")]
|
[assembly: AssemblyCompany("Clemens Fischer")]
|
||||||
[assembly: AssemblyCopyright("Copyright © 2020 Clemens Fischer")]
|
[assembly: AssemblyCopyright("Copyright © 2020 Clemens Fischer")]
|
||||||
[assembly: AssemblyTrademark("")]
|
[assembly: AssemblyTrademark("")]
|
||||||
[assembly: AssemblyVersion("4.17.0")]
|
[assembly: AssemblyVersion("5.0.0")]
|
||||||
[assembly: AssemblyFileVersion("4.17.0")]
|
[assembly: AssemblyFileVersion("5.0.0")]
|
||||||
[assembly: AssemblyConfiguration("")]
|
[assembly: AssemblyConfiguration("")]
|
||||||
[assembly: AssemblyCulture("")]
|
[assembly: AssemblyCulture("")]
|
||||||
[assembly: ComVisible(false)]
|
[assembly: ComVisible(false)]
|
||||||
|
|
|
||||||
|
|
@ -25,11 +25,11 @@ namespace MapControl.Projections
|
||||||
private ICoordinateSystem coordinateSystem;
|
private ICoordinateSystem coordinateSystem;
|
||||||
private bool isNormalCylindrical;
|
private bool isNormalCylindrical;
|
||||||
private bool isWebMercator;
|
private bool isWebMercator;
|
||||||
private double trueScale;
|
private double unitsPerDegree;
|
||||||
private string bboxFormat;
|
private string bboxFormat;
|
||||||
|
|
||||||
public IMathTransform LocationToPointTransform { get; private set; }
|
public IMathTransform LocationToMapTransform { get; private set; }
|
||||||
public IMathTransform PointToLocationTransform { get; private set; }
|
public IMathTransform MapToLocationTransform { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the ICoordinateSystem of the MapProjection.
|
/// Gets or sets the ICoordinateSystem of the MapProjection.
|
||||||
|
|
@ -48,11 +48,11 @@ namespace MapControl.Projections
|
||||||
|
|
||||||
var transformFactory = new CoordinateTransformationFactory();
|
var transformFactory = new CoordinateTransformationFactory();
|
||||||
|
|
||||||
LocationToPointTransform = transformFactory
|
LocationToMapTransform = transformFactory
|
||||||
.CreateFromCoordinateSystems(GeographicCoordinateSystem.WGS84, coordinateSystem)
|
.CreateFromCoordinateSystems(GeographicCoordinateSystem.WGS84, coordinateSystem)
|
||||||
.MathTransform;
|
.MathTransform;
|
||||||
|
|
||||||
PointToLocationTransform = transformFactory
|
MapToLocationTransform = transformFactory
|
||||||
.CreateFromCoordinateSystems(coordinateSystem, GeographicCoordinateSystem.WGS84)
|
.CreateFromCoordinateSystems(coordinateSystem, GeographicCoordinateSystem.WGS84)
|
||||||
.MathTransform;
|
.MathTransform;
|
||||||
|
|
||||||
|
|
@ -76,14 +76,14 @@ namespace MapControl.Projections
|
||||||
(falseEasting == null || falseEasting.Value == 0d) &&
|
(falseEasting == null || falseEasting.Value == 0d) &&
|
||||||
(falseNorthing == null || falseNorthing.Value == 0d);
|
(falseNorthing == null || falseNorthing.Value == 0d);
|
||||||
isWebMercator = CrsId == "EPSG:3857" || CrsId == "EPSG:900913";
|
isWebMercator = CrsId == "EPSG:3857" || CrsId == "EPSG:900913";
|
||||||
trueScale = (scaleFactor != null ? scaleFactor.Value : 1d) * Wgs84MetersPerDegree;
|
unitsPerDegree = (scaleFactor != null ? scaleFactor.Value : 1d) * Wgs84MetersPerDegree;
|
||||||
bboxFormat = "{0},{1},{2},{3}";
|
bboxFormat = "{0},{1},{2},{3}";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
isNormalCylindrical = true;
|
isNormalCylindrical = true;
|
||||||
isWebMercator = false;
|
isWebMercator = false;
|
||||||
trueScale = 1d;
|
unitsPerDegree = 1d;
|
||||||
bboxFormat = "{1},{0},{3},{2}";
|
bboxFormat = "{1},{0},{3},{2}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -110,31 +110,31 @@ namespace MapControl.Projections
|
||||||
get { return isWebMercator; }
|
get { return isWebMercator; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public override double TrueScale
|
public override double UnitsPerDegree
|
||||||
{
|
{
|
||||||
get { return trueScale; }
|
get { return unitsPerDegree; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Point LocationToPoint(Location location)
|
public override Point LocationToMap(Location location)
|
||||||
{
|
{
|
||||||
if (LocationToPointTransform == null)
|
if (LocationToMapTransform == null)
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException("The CoordinateSystem property is not set.");
|
throw new InvalidOperationException("The CoordinateSystem property is not set.");
|
||||||
}
|
}
|
||||||
|
|
||||||
var coordinate = LocationToPointTransform.Transform(new Coordinate(location.Longitude, location.Latitude));
|
var coordinate = LocationToMapTransform.Transform(new Coordinate(location.Longitude, location.Latitude));
|
||||||
|
|
||||||
return new Point(coordinate.X, coordinate.Y);
|
return new Point(coordinate.X, coordinate.Y);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Location PointToLocation(Point point)
|
public override Location MapToLocation(Point point)
|
||||||
{
|
{
|
||||||
if (PointToLocationTransform == null)
|
if (MapToLocationTransform == null)
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException("The CoordinateSystem property is not set.");
|
throw new InvalidOperationException("The CoordinateSystem property is not set.");
|
||||||
}
|
}
|
||||||
|
|
||||||
var coordinate = PointToLocationTransform.Transform(new Coordinate(point.X, point.Y));
|
var coordinate = MapToLocationTransform.Transform(new Coordinate(point.X, point.Y));
|
||||||
|
|
||||||
return new Location(coordinate.Y, coordinate.X);
|
return new Location(coordinate.Y, coordinate.X);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,12 +33,12 @@ namespace MapControl.Projections
|
||||||
this.falseNorthing = falseNorthing;
|
this.falseNorthing = falseNorthing;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override double TrueScale
|
public override double UnitsPerDegree
|
||||||
{
|
{
|
||||||
get { return scaleFactor * Wgs84MetersPerDegree; }
|
get { return scaleFactor * Wgs84MetersPerDegree; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Vector GetMapScale(Location location)
|
public override Vector GetRelativeScale(Location location)
|
||||||
{
|
{
|
||||||
var lat = (north ? location.Latitude : -location.Latitude) * Math.PI / 180d;
|
var lat = (north ? location.Latitude : -location.Latitude) * Math.PI / 180d;
|
||||||
var a = Wgs84EquatorialRadius;
|
var a = Wgs84EquatorialRadius;
|
||||||
|
|
@ -50,10 +50,10 @@ namespace MapControl.Projections
|
||||||
var m = Math.Cos(lat) / Math.Sqrt(1d - eSinLat * eSinLat);
|
var m = Math.Cos(lat) / Math.Sqrt(1d - eSinLat * eSinLat);
|
||||||
var k = rho / (a * m);
|
var k = rho / (a * m);
|
||||||
|
|
||||||
return new Vector(ViewportScale * k, ViewportScale * k);
|
return new Vector(k, k);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Point LocationToPoint(Location location)
|
public override Point LocationToMap(Location location)
|
||||||
{
|
{
|
||||||
var lat = location.Latitude * Math.PI / 180d;
|
var lat = location.Latitude * Math.PI / 180d;
|
||||||
var lon = location.Longitude * Math.PI / 180d;
|
var lon = location.Longitude * Math.PI / 180d;
|
||||||
|
|
@ -76,7 +76,7 @@ namespace MapControl.Projections
|
||||||
return new Point(rho * Math.Sin(lon) + falseEasting, rho * Math.Cos(lon) + falseNorthing);
|
return new Point(rho * Math.Sin(lon) + falseEasting, rho * Math.Cos(lon) + falseNorthing);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Location PointToLocation(Point point)
|
public override Location MapToLocation(Point point)
|
||||||
{
|
{
|
||||||
point.X -= falseEasting;
|
point.X -= falseEasting;
|
||||||
point.Y -= falseNorthing;
|
point.Y -= falseNorthing;
|
||||||
|
|
|
||||||
|
|
@ -21,11 +21,11 @@ namespace MapControl.Projections
|
||||||
CoordinateSystem = ProjectedCoordinateSystem.WebMercator;
|
CoordinateSystem = ProjectedCoordinateSystem.WebMercator;
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -37,13 +37,13 @@ namespace MapControl.Projections
|
||||||
"AUTHORITY[\"EPSG\",\"3395\"]]";
|
"AUTHORITY[\"EPSG\",\"3395\"]]";
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Vector GetMapScale(Location location)
|
public override Vector GetRelativeScale(Location location)
|
||||||
{
|
{
|
||||||
var lat = location.Latitude * Math.PI / 180d;
|
var lat = location.Latitude * Math.PI / 180d;
|
||||||
var eSinLat = Wgs84Eccentricity * Math.Sin(lat);
|
var eSinLat = Wgs84Eccentricity * Math.Sin(lat);
|
||||||
var k = Math.Sqrt(1d - eSinLat * eSinLat) / Math.Cos(lat); // p.44 (7-8)
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,8 @@ using System.Runtime.InteropServices;
|
||||||
[assembly: AssemblyCompany("Clemens Fischer")]
|
[assembly: AssemblyCompany("Clemens Fischer")]
|
||||||
[assembly: AssemblyCopyright("Copyright © 2020 Clemens Fischer")]
|
[assembly: AssemblyCopyright("Copyright © 2020 Clemens Fischer")]
|
||||||
[assembly: AssemblyTrademark("")]
|
[assembly: AssemblyTrademark("")]
|
||||||
[assembly: AssemblyVersion("4.17.0")]
|
[assembly: AssemblyVersion("5.0.0")]
|
||||||
[assembly: AssemblyFileVersion("4.17.0")]
|
[assembly: AssemblyFileVersion("5.0.0")]
|
||||||
[assembly: AssemblyConfiguration("")]
|
[assembly: AssemblyConfiguration("")]
|
||||||
[assembly: AssemblyCulture("")]
|
[assembly: AssemblyCulture("")]
|
||||||
[assembly: ComVisible(false)]
|
[assembly: ComVisible(false)]
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,8 @@ using System.Runtime.InteropServices;
|
||||||
[assembly: AssemblyCompany("Clemens Fischer")]
|
[assembly: AssemblyCompany("Clemens Fischer")]
|
||||||
[assembly: AssemblyCopyright("Copyright © 2020 Clemens Fischer")]
|
[assembly: AssemblyCopyright("Copyright © 2020 Clemens Fischer")]
|
||||||
[assembly: AssemblyTrademark("")]
|
[assembly: AssemblyTrademark("")]
|
||||||
[assembly: AssemblyVersion("4.17.0")]
|
[assembly: AssemblyVersion("5.0.0")]
|
||||||
[assembly: AssemblyFileVersion("4.17.0")]
|
[assembly: AssemblyFileVersion("5.0.0")]
|
||||||
[assembly: AssemblyConfiguration("")]
|
[assembly: AssemblyConfiguration("")]
|
||||||
[assembly: AssemblyCulture("")]
|
[assembly: AssemblyCulture("")]
|
||||||
[assembly: ComVisible(false)]
|
[assembly: ComVisible(false)]
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,8 @@ using System.Runtime.InteropServices;
|
||||||
[assembly: AssemblyCompany("Clemens Fischer")]
|
[assembly: AssemblyCompany("Clemens Fischer")]
|
||||||
[assembly: AssemblyCopyright("Copyright © 2020 Clemens Fischer")]
|
[assembly: AssemblyCopyright("Copyright © 2020 Clemens Fischer")]
|
||||||
[assembly: AssemblyTrademark("")]
|
[assembly: AssemblyTrademark("")]
|
||||||
[assembly: AssemblyVersion("4.17.0")]
|
[assembly: AssemblyVersion("5.0.0")]
|
||||||
[assembly: AssemblyFileVersion("4.17.0")]
|
[assembly: AssemblyFileVersion("5.0.0")]
|
||||||
[assembly: AssemblyConfiguration("")]
|
[assembly: AssemblyConfiguration("")]
|
||||||
[assembly: AssemblyCulture("")]
|
[assembly: AssemblyCulture("")]
|
||||||
[assembly: ComVisible(false)]
|
[assembly: ComVisible(false)]
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,8 @@ using System.Runtime.InteropServices;
|
||||||
[assembly: AssemblyCompany("Clemens Fischer")]
|
[assembly: AssemblyCompany("Clemens Fischer")]
|
||||||
[assembly: AssemblyCopyright("Copyright © 2020 Clemens Fischer")]
|
[assembly: AssemblyCopyright("Copyright © 2020 Clemens Fischer")]
|
||||||
[assembly: AssemblyTrademark("")]
|
[assembly: AssemblyTrademark("")]
|
||||||
[assembly: AssemblyVersion("4.17.0")]
|
[assembly: AssemblyVersion("5.0.0")]
|
||||||
[assembly: AssemblyFileVersion("4.17.0")]
|
[assembly: AssemblyFileVersion("5.0.0")]
|
||||||
[assembly: AssemblyConfiguration("")]
|
[assembly: AssemblyConfiguration("")]
|
||||||
[assembly: AssemblyCulture("")]
|
[assembly: AssemblyCulture("")]
|
||||||
[assembly: ComVisible(false)]
|
[assembly: ComVisible(false)]
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,7 @@ namespace ProjectionDemo
|
||||||
var map = (MapBase)sender;
|
var map = (MapBase)sender;
|
||||||
var pos = e.GetPosition(map);
|
var pos = e.GetPosition(map);
|
||||||
|
|
||||||
viewModel.PushpinLocation = map.MapProjection.ViewportPointToLocation(pos);
|
viewModel.PushpinLocation = map.ViewportPointToLocation(pos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
<OutputType>WinExe</OutputType>
|
<OutputType>WinExe</OutputType>
|
||||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||||
<UseWPF>true</UseWPF>
|
<UseWPF>true</UseWPF>
|
||||||
<Version>4.17.0</Version>
|
<Version>5.0.0</Version>
|
||||||
<Authors>Clemens Fischer</Authors>
|
<Authors>Clemens Fischer</Authors>
|
||||||
<Description>XAML Map Control Map Projection Demo Application</Description>
|
<Description>XAML Map Control Map Projection Demo Application</Description>
|
||||||
<Product>XAML Map Control</Product>
|
<Product>XAML Map Control</Product>
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ using System.Runtime.InteropServices;
|
||||||
[assembly: AssemblyCopyright("Copyright © 2020 Clemens Fischer")]
|
[assembly: AssemblyCopyright("Copyright © 2020 Clemens Fischer")]
|
||||||
[assembly: AssemblyTrademark("")]
|
[assembly: AssemblyTrademark("")]
|
||||||
[assembly: AssemblyCulture("")]
|
[assembly: AssemblyCulture("")]
|
||||||
[assembly: AssemblyVersion("4.17.0")]
|
[assembly: AssemblyVersion("5.0.0")]
|
||||||
[assembly: AssemblyFileVersion("4.17.0")]
|
[assembly: AssemblyFileVersion("5.0.0")]
|
||||||
[assembly: AssemblyConfiguration("")]
|
[assembly: AssemblyConfiguration("")]
|
||||||
[assembly: ComVisible(false)]
|
[assembly: ComVisible(false)]
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,8 @@ using System.Runtime.InteropServices;
|
||||||
[assembly: AssemblyCompany("Clemens Fischer")]
|
[assembly: AssemblyCompany("Clemens Fischer")]
|
||||||
[assembly: AssemblyCopyright("Copyright © 2020 Clemens Fischer")]
|
[assembly: AssemblyCopyright("Copyright © 2020 Clemens Fischer")]
|
||||||
[assembly: AssemblyTrademark("")]
|
[assembly: AssemblyTrademark("")]
|
||||||
[assembly: AssemblyVersion("4.17.0")]
|
[assembly: AssemblyVersion("5.0.0")]
|
||||||
[assembly: AssemblyFileVersion("4.17.0")]
|
[assembly: AssemblyFileVersion("5.0.0")]
|
||||||
[assembly: AssemblyConfiguration("")]
|
[assembly: AssemblyConfiguration("")]
|
||||||
[assembly: AssemblyCulture("")]
|
[assembly: AssemblyCulture("")]
|
||||||
[assembly: ComVisible(false)]
|
[assembly: ComVisible(false)]
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
<UseWPF>true</UseWPF>
|
<UseWPF>true</UseWPF>
|
||||||
<RootNamespace>WpfCoreApp</RootNamespace>
|
<RootNamespace>WpfCoreApp</RootNamespace>
|
||||||
<Product>XAML Map Control</Product>
|
<Product>XAML Map Control</Product>
|
||||||
<Version>4.17.0</Version>
|
<Version>5.0.0</Version>
|
||||||
<Description>XAML Map Control WPF Sample Application</Description>
|
<Description>XAML Map Control WPF Sample Application</Description>
|
||||||
<Authors>Clemens Fischer</Authors>
|
<Authors>Clemens Fischer</Authors>
|
||||||
<Copyright>Copyright © 2020 Clemens Fischer</Copyright>
|
<Copyright>Copyright © 2020 Clemens Fischer</Copyright>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue