mirror of
https://github.com/ClemensFischer/XAML-Map-Control.git
synced 2025-12-06 07:12:04 +01:00
Workaround for rounding errors in MapTransform.
This commit is contained in:
parent
3717081f18
commit
4a37e94d77
|
|
@ -79,7 +79,7 @@ namespace MapControl
|
||||||
if (mousePosition.HasValue)
|
if (mousePosition.HasValue)
|
||||||
{
|
{
|
||||||
var position = e.GetPosition(this);
|
var position = e.GetPosition(this);
|
||||||
TranslateMap((Point)(position - mousePosition.Value));
|
TranslateMap(position - mousePosition.Value);
|
||||||
mousePosition = position;
|
mousePosition = position;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -96,7 +96,7 @@ namespace MapControl
|
||||||
base.OnManipulationDelta(e);
|
base.OnManipulationDelta(e);
|
||||||
|
|
||||||
TransformMap(e.ManipulationOrigin,
|
TransformMap(e.ManipulationOrigin,
|
||||||
(Point)e.DeltaManipulation.Translation, e.DeltaManipulation.Rotation,
|
e.DeltaManipulation.Translation, e.DeltaManipulation.Rotation,
|
||||||
(e.DeltaManipulation.Scale.X + e.DeltaManipulation.Scale.Y) / 2d);
|
(e.DeltaManipulation.Scale.X + e.DeltaManipulation.Scale.Y) / 2d);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -70,11 +70,12 @@ namespace MapControl
|
||||||
MapOrigin = mapTransform.Transform(origin);
|
MapOrigin = mapTransform.Transform(origin);
|
||||||
ViewportScale = Math.Pow(2d, ZoomLevel) * (double)TileSource.TileSize / 360d;
|
ViewportScale = Math.Pow(2d, ZoomLevel) * (double)TileSource.TileSize / 360d;
|
||||||
|
|
||||||
viewportTransform.Matrix =
|
var transform = new Matrix(1d, 0d, 0d, 1d, -MapOrigin.X, -MapOrigin.Y)
|
||||||
new Matrix(1d, 0d, 0d, 1d, -MapOrigin.X, -MapOrigin.Y)
|
.Rotate(-Heading)
|
||||||
.Scale(ViewportScale, -ViewportScale)
|
.Scale(ViewportScale, -ViewportScale)
|
||||||
.Rotate(Heading)
|
|
||||||
.Translate(ViewportOrigin.X, ViewportOrigin.Y);
|
.Translate(ViewportOrigin.X, ViewportOrigin.Y);
|
||||||
|
|
||||||
|
viewportTransform.Matrix = transform;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,24 @@ namespace MapControl
|
||||||
typeof(MapBase), new FrameworkPropertyMetadata(Brushes.Transparent));
|
typeof(MapBase), new FrameworkPropertyMetadata(Brushes.Transparent));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Changes the Center property according to the specified translation in viewport coordinates.
|
||||||
|
/// </summary>
|
||||||
|
public void TranslateMap(Vector translation)
|
||||||
|
{
|
||||||
|
TranslateMap((Point)translation);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Changes the Center, Heading and ZoomLevel properties according to the specified
|
||||||
|
/// viewport coordinate translation, rotation and scale delta values. Rotation and scaling
|
||||||
|
/// is performed relative to the specified origin point in viewport coordinates.
|
||||||
|
/// </summary>
|
||||||
|
public void TransformMap(Point origin, Vector translation, double rotation, double scale)
|
||||||
|
{
|
||||||
|
TransformMap(origin, (Point)translation, rotation, scale);
|
||||||
|
}
|
||||||
|
|
||||||
partial void RemoveAnimation(DependencyProperty property)
|
partial void RemoveAnimation(DependencyProperty property)
|
||||||
{
|
{
|
||||||
BeginAnimation(property, null);
|
BeginAnimation(property, null);
|
||||||
|
|
@ -72,8 +90,8 @@ namespace MapControl
|
||||||
ViewportScale = Math.Pow(2d, ZoomLevel) * (double)TileSource.TileSize / 360d;
|
ViewportScale = Math.Pow(2d, ZoomLevel) * (double)TileSource.TileSize / 360d;
|
||||||
|
|
||||||
var transform = new Matrix(1d, 0d, 0d, 1d, -MapOrigin.X, -MapOrigin.Y);
|
var transform = new Matrix(1d, 0d, 0d, 1d, -MapOrigin.X, -MapOrigin.Y);
|
||||||
|
transform.Rotate(-Heading);
|
||||||
transform.Scale(ViewportScale, -ViewportScale);
|
transform.Scale(ViewportScale, -ViewportScale);
|
||||||
transform.Rotate(Heading);
|
|
||||||
transform.Translate(ViewportOrigin.X, ViewportOrigin.Y);
|
transform.Translate(ViewportOrigin.X, ViewportOrigin.Y);
|
||||||
|
|
||||||
viewportTransform.Matrix = transform;
|
viewportTransform.Matrix = transform;
|
||||||
|
|
|
||||||
|
|
@ -75,9 +75,6 @@ namespace MapControl
|
||||||
private DoubleAnimation headingAnimation;
|
private DoubleAnimation headingAnimation;
|
||||||
private bool internalPropertyChange;
|
private bool internalPropertyChange;
|
||||||
|
|
||||||
internal Point MapOrigin { get; private set; }
|
|
||||||
internal Point ViewportOrigin { get; private set; }
|
|
||||||
|
|
||||||
public MapBase()
|
public MapBase()
|
||||||
{
|
{
|
||||||
Initialize();
|
Initialize();
|
||||||
|
|
@ -272,6 +269,9 @@ namespace MapControl
|
||||||
get { return scaleRotateTransform; }
|
get { return scaleRotateTransform; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal Point MapOrigin { get; private set; }
|
||||||
|
internal Point ViewportOrigin { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the scaling factor from cartesian map coordinates to viewport coordinates.
|
/// Gets the scaling factor from cartesian map coordinates to viewport coordinates.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -337,18 +337,32 @@ namespace MapControl
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Changes the Center property according to the specified translation in viewport coordinates.
|
/// Changes the Center property according to the specified map translation in viewport coordinates.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void TranslateMap(Point translation)
|
public void TranslateMap(Point translation)
|
||||||
{
|
{
|
||||||
if (transformOrigin != null)
|
if (transformOrigin != null)
|
||||||
{
|
{
|
||||||
ResetTransformOrigin();
|
ResetTransformOrigin();
|
||||||
|
UpdateTransform();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (translation.X != 0d || translation.Y != 0d)
|
if (translation.X != 0d || translation.Y != 0d)
|
||||||
{
|
{
|
||||||
Center = ViewportPointToLocation(new Point(ViewportOrigin.X - translation.X, ViewportOrigin.Y - translation.Y));
|
if (Heading != 0d)
|
||||||
|
{
|
||||||
|
var cos = Math.Cos(Heading / 180d * Math.PI);
|
||||||
|
var sin = Math.Sin(Heading / 180d * Math.PI);
|
||||||
|
|
||||||
|
translation = new Point(
|
||||||
|
translation.X * cos + translation.Y * sin,
|
||||||
|
translation.Y * cos - translation.X * sin);
|
||||||
|
}
|
||||||
|
|
||||||
|
translation.X /= -ViewportScale;
|
||||||
|
translation.Y /= ViewportScale;
|
||||||
|
|
||||||
|
Center = mapTransform.Transform(Center, MapOrigin, translation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -359,9 +373,10 @@ namespace MapControl
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void TransformMap(Point origin, Point translation, double rotation, double scale)
|
public void TransformMap(Point origin, Point translation, double rotation, double scale)
|
||||||
{
|
{
|
||||||
SetTransformOrigin(origin);
|
if (rotation != 0d || scale != 1d)
|
||||||
|
{
|
||||||
ViewportOrigin = new Point(ViewportOrigin.X + translation.X, ViewportOrigin.Y + translation.Y);
|
transformOrigin = ViewportPointToLocation(origin);
|
||||||
|
ViewportOrigin = new Point(origin.X + translation.X, origin.Y + translation.Y);
|
||||||
|
|
||||||
if (rotation != 0d)
|
if (rotation != 0d)
|
||||||
{
|
{
|
||||||
|
|
@ -379,6 +394,11 @@ namespace MapControl
|
||||||
|
|
||||||
UpdateTransform(true);
|
UpdateTransform(true);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
TranslateMap(translation); // more precise
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sets the value of the TargetZoomLevel property while retaining the specified origin point
|
/// Sets the value of the TargetZoomLevel property while retaining the specified origin point
|
||||||
|
|
@ -567,7 +587,6 @@ namespace MapControl
|
||||||
if (!internalPropertyChange)
|
if (!internalPropertyChange)
|
||||||
{
|
{
|
||||||
AdjustCenterProperty(CenterProperty, ref center);
|
AdjustCenterProperty(CenterProperty, ref center);
|
||||||
ResetTransformOrigin();
|
|
||||||
UpdateTransform();
|
UpdateTransform();
|
||||||
|
|
||||||
if (centerAnimation == null)
|
if (centerAnimation == null)
|
||||||
|
|
@ -616,8 +635,6 @@ namespace MapControl
|
||||||
InternalSetValue(CenterProperty, TargetCenter);
|
InternalSetValue(CenterProperty, TargetCenter);
|
||||||
InternalSetValue(CenterPointProperty, mapTransform.Transform(TargetCenter));
|
InternalSetValue(CenterPointProperty, mapTransform.Transform(TargetCenter));
|
||||||
RemoveAnimation(CenterPointProperty); // remove holding animation in WPF
|
RemoveAnimation(CenterPointProperty); // remove holding animation in WPF
|
||||||
|
|
||||||
ResetTransformOrigin();
|
|
||||||
UpdateTransform();
|
UpdateTransform();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -628,7 +645,6 @@ namespace MapControl
|
||||||
{
|
{
|
||||||
centerPoint.X = Location.NormalizeLongitude(centerPoint.X);
|
centerPoint.X = Location.NormalizeLongitude(centerPoint.X);
|
||||||
InternalSetValue(CenterProperty, mapTransform.Transform(centerPoint));
|
InternalSetValue(CenterProperty, mapTransform.Transform(centerPoint));
|
||||||
ResetTransformOrigin();
|
|
||||||
UpdateTransform();
|
UpdateTransform();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -720,7 +736,7 @@ namespace MapControl
|
||||||
InternalSetValue(ZoomLevelProperty, TargetZoomLevel);
|
InternalSetValue(ZoomLevelProperty, TargetZoomLevel);
|
||||||
RemoveAnimation(ZoomLevelProperty); // remove holding animation in WPF
|
RemoveAnimation(ZoomLevelProperty); // remove holding animation in WPF
|
||||||
|
|
||||||
UpdateTransform(true);
|
Dispatcher.BeginInvoke(() => UpdateTransform(true));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -793,12 +809,11 @@ namespace MapControl
|
||||||
|
|
||||||
InternalSetValue(HeadingProperty, TargetHeading);
|
InternalSetValue(HeadingProperty, TargetHeading);
|
||||||
RemoveAnimation(HeadingProperty); // remove holding animation in WPF
|
RemoveAnimation(HeadingProperty); // remove holding animation in WPF
|
||||||
|
|
||||||
UpdateTransform();
|
UpdateTransform();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateTransform(bool resetTransformOrigin = false)
|
private void UpdateTransform(bool resetOrigin = false)
|
||||||
{
|
{
|
||||||
var center = transformOrigin ?? Center;
|
var center = transformOrigin ?? Center;
|
||||||
|
|
||||||
|
|
@ -812,7 +827,7 @@ namespace MapControl
|
||||||
if (center.Latitude < -mapTransform.MaxLatitude || center.Latitude > mapTransform.MaxLatitude)
|
if (center.Latitude < -mapTransform.MaxLatitude || center.Latitude > mapTransform.MaxLatitude)
|
||||||
{
|
{
|
||||||
center.Latitude = Math.Min(Math.Max(center.Latitude, -mapTransform.MaxLatitude), mapTransform.MaxLatitude);
|
center.Latitude = Math.Min(Math.Max(center.Latitude, -mapTransform.MaxLatitude), mapTransform.MaxLatitude);
|
||||||
resetTransformOrigin = true;
|
resetOrigin = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
InternalSetValue(CenterProperty, center);
|
InternalSetValue(CenterProperty, center);
|
||||||
|
|
@ -823,7 +838,7 @@ namespace MapControl
|
||||||
InternalSetValue(CenterPointProperty, mapTransform.Transform(center));
|
InternalSetValue(CenterPointProperty, mapTransform.Transform(center));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (resetTransformOrigin)
|
if (resetOrigin)
|
||||||
{
|
{
|
||||||
ResetTransformOrigin();
|
ResetTransformOrigin();
|
||||||
SetViewportTransform(center);
|
SetViewportTransform(center);
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
// Licensed under the Microsoft Public License (Ms-PL)
|
// Licensed under the Microsoft Public License (Ms-PL)
|
||||||
|
|
||||||
#if NETFX_CORE
|
#if NETFX_CORE
|
||||||
|
using System;
|
||||||
using Windows.Foundation;
|
using Windows.Foundation;
|
||||||
#else
|
#else
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
|
|
@ -25,21 +26,21 @@ namespace MapControl
|
||||||
public abstract double MaxLatitude { get; }
|
public abstract double MaxLatitude { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the scale factor of the map projection at the specified geographic location
|
/// Gets the scale factor of the map projection at the specified
|
||||||
/// relative to the scale at latitude zero.
|
/// geographic location relative to the scale at latitude zero.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract double RelativeScale(Location location);
|
public abstract double RelativeScale(Location location);
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Transforms a cartesian coordinate point to a geographic location.
|
|
||||||
/// </summary>
|
|
||||||
public abstract Location Transform(Point point);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Transforms a geographic location to a cartesian coordinate point.
|
/// Transforms a geographic location to a cartesian coordinate point.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract Point Transform(Location location);
|
public abstract Point Transform(Location location);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Transforms a cartesian coordinate point to a geographic location.
|
||||||
|
/// </summary>
|
||||||
|
public abstract Location Transform(Point point);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Transforms a geographic location to a cartesian coordinate point
|
/// Transforms a geographic location to a cartesian coordinate point
|
||||||
/// with minumum distance to the specified reference longitude value.
|
/// with minumum distance to the specified reference longitude value.
|
||||||
|
|
@ -61,5 +62,22 @@ namespace MapControl
|
||||||
|
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Transforms a geographic location by the specified translation in viewport coordinates.
|
||||||
|
/// </summary>
|
||||||
|
public Location Transform(Location origin, Point mapOrigin, Point translation)
|
||||||
|
{
|
||||||
|
#if NETFX_CORE
|
||||||
|
var latitudeTranslation = translation.Y / RelativeScale(origin);
|
||||||
|
|
||||||
|
if (Math.Abs(latitudeTranslation) < 1e-3) // avoid rounding errors
|
||||||
|
{
|
||||||
|
return new Location(origin.Latitude + latitudeTranslation, origin.Longitude + translation.X);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return Transform(new Point(mapOrigin.X + translation.X, mapOrigin.Y + translation.Y));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ namespace MapControl
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class MercatorTransform : MapTransform
|
public class MercatorTransform : MapTransform
|
||||||
{
|
{
|
||||||
public static readonly double MaxLatitudeValue = Math.Atan(Math.Sinh(Math.PI)) / Math.PI * 180d;
|
public static readonly double MaxLatitudeValue = YToLatitude(180d);
|
||||||
|
|
||||||
public static double RelativeScale(double latitude)
|
public static double RelativeScale(double latitude)
|
||||||
{
|
{
|
||||||
|
|
@ -46,13 +46,12 @@ namespace MapControl
|
||||||
return double.PositiveInfinity;
|
return double.PositiveInfinity;
|
||||||
}
|
}
|
||||||
|
|
||||||
latitude *= Math.PI / 180d;
|
return Math.Log(Math.Tan((latitude + 90d) * Math.PI / 360d)) / Math.PI * 180d;
|
||||||
return Math.Log(Math.Tan(latitude) + 1d / Math.Cos(latitude)) / Math.PI * 180d;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static double YToLatitude(double y)
|
public static double YToLatitude(double y)
|
||||||
{
|
{
|
||||||
return Math.Atan(Math.Sinh(y * Math.PI / 180d)) / Math.PI * 180d;
|
return Math.Atan(Math.Exp(y * Math.PI / 180d)) / Math.PI * 360d - 90d;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override double MaxLatitude
|
public override double MaxLatitude
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,7 @@ namespace MapControl
|
||||||
new Matrix(1d, 0d, 0d, 1d, TileSource.TileSize * TileGrid.XMin, TileSource.TileSize * TileGrid.YMin)
|
new Matrix(1d, 0d, 0d, 1d, TileSource.TileSize * TileGrid.XMin, TileSource.TileSize * TileGrid.YMin)
|
||||||
.Scale(scale, scale)
|
.Scale(scale, scale)
|
||||||
.Translate(offsetX, offsetY)
|
.Translate(offsetX, offsetY)
|
||||||
.RotateAt(parentMap.Heading, parentMap.ViewportOrigin.X, parentMap.ViewportOrigin.Y); ;
|
.RotateAt(parentMap.Heading, parentMap.ViewportOrigin.X, parentMap.ViewportOrigin.Y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue