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)
|
||||
{
|
||||
var position = e.GetPosition(this);
|
||||
TranslateMap((Point)(position - mousePosition.Value));
|
||||
TranslateMap(position - mousePosition.Value);
|
||||
mousePosition = position;
|
||||
}
|
||||
}
|
||||
|
|
@ -96,7 +96,7 @@ namespace MapControl
|
|||
base.OnManipulationDelta(e);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -70,11 +70,12 @@ namespace MapControl
|
|||
MapOrigin = mapTransform.Transform(origin);
|
||||
ViewportScale = Math.Pow(2d, ZoomLevel) * (double)TileSource.TileSize / 360d;
|
||||
|
||||
viewportTransform.Matrix =
|
||||
new Matrix(1d, 0d, 0d, 1d, -MapOrigin.X, -MapOrigin.Y)
|
||||
var transform = new Matrix(1d, 0d, 0d, 1d, -MapOrigin.X, -MapOrigin.Y)
|
||||
.Rotate(-Heading)
|
||||
.Scale(ViewportScale, -ViewportScale)
|
||||
.Rotate(Heading)
|
||||
.Translate(ViewportOrigin.X, ViewportOrigin.Y);
|
||||
|
||||
viewportTransform.Matrix = transform;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,6 +53,24 @@ namespace MapControl
|
|||
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)
|
||||
{
|
||||
BeginAnimation(property, null);
|
||||
|
|
@ -72,8 +90,8 @@ namespace MapControl
|
|||
ViewportScale = Math.Pow(2d, ZoomLevel) * (double)TileSource.TileSize / 360d;
|
||||
|
||||
var transform = new Matrix(1d, 0d, 0d, 1d, -MapOrigin.X, -MapOrigin.Y);
|
||||
transform.Rotate(-Heading);
|
||||
transform.Scale(ViewportScale, -ViewportScale);
|
||||
transform.Rotate(Heading);
|
||||
transform.Translate(ViewportOrigin.X, ViewportOrigin.Y);
|
||||
|
||||
viewportTransform.Matrix = transform;
|
||||
|
|
|
|||
|
|
@ -75,9 +75,6 @@ namespace MapControl
|
|||
private DoubleAnimation headingAnimation;
|
||||
private bool internalPropertyChange;
|
||||
|
||||
internal Point MapOrigin { get; private set; }
|
||||
internal Point ViewportOrigin { get; private set; }
|
||||
|
||||
public MapBase()
|
||||
{
|
||||
Initialize();
|
||||
|
|
@ -272,6 +269,9 @@ namespace MapControl
|
|||
get { return scaleRotateTransform; }
|
||||
}
|
||||
|
||||
internal Point MapOrigin { get; private set; }
|
||||
internal Point ViewportOrigin { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the scaling factor from cartesian map coordinates to viewport coordinates.
|
||||
/// </summary>
|
||||
|
|
@ -337,18 +337,32 @@ namespace MapControl
|
|||
}
|
||||
|
||||
/// <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>
|
||||
public void TranslateMap(Point translation)
|
||||
{
|
||||
if (transformOrigin != null)
|
||||
{
|
||||
ResetTransformOrigin();
|
||||
UpdateTransform();
|
||||
}
|
||||
|
||||
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,25 +373,31 @@ namespace MapControl
|
|||
/// </summary>
|
||||
public void TransformMap(Point origin, Point translation, double rotation, double scale)
|
||||
{
|
||||
SetTransformOrigin(origin);
|
||||
|
||||
ViewportOrigin = new Point(ViewportOrigin.X + translation.X, ViewportOrigin.Y + translation.Y);
|
||||
|
||||
if (rotation != 0d)
|
||||
if (rotation != 0d || scale != 1d)
|
||||
{
|
||||
var heading = (((Heading + rotation) % 360d) + 360d) % 360d;
|
||||
InternalSetValue(HeadingProperty, heading);
|
||||
InternalSetValue(TargetHeadingProperty, heading);
|
||||
}
|
||||
transformOrigin = ViewportPointToLocation(origin);
|
||||
ViewportOrigin = new Point(origin.X + translation.X, origin.Y + translation.Y);
|
||||
|
||||
if (scale != 1d)
|
||||
if (rotation != 0d)
|
||||
{
|
||||
var heading = (((Heading + rotation) % 360d) + 360d) % 360d;
|
||||
InternalSetValue(HeadingProperty, heading);
|
||||
InternalSetValue(TargetHeadingProperty, heading);
|
||||
}
|
||||
|
||||
if (scale != 1d)
|
||||
{
|
||||
var zoomLevel = Math.Min(Math.Max(ZoomLevel + Math.Log(scale, 2d), MinZoomLevel), MaxZoomLevel);
|
||||
InternalSetValue(ZoomLevelProperty, zoomLevel);
|
||||
InternalSetValue(TargetZoomLevelProperty, zoomLevel);
|
||||
}
|
||||
|
||||
UpdateTransform(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
var zoomLevel = Math.Min(Math.Max(ZoomLevel + Math.Log(scale, 2d), MinZoomLevel), MaxZoomLevel);
|
||||
InternalSetValue(ZoomLevelProperty, zoomLevel);
|
||||
InternalSetValue(TargetZoomLevelProperty, zoomLevel);
|
||||
TranslateMap(translation); // more precise
|
||||
}
|
||||
|
||||
UpdateTransform(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -567,7 +587,6 @@ namespace MapControl
|
|||
if (!internalPropertyChange)
|
||||
{
|
||||
AdjustCenterProperty(CenterProperty, ref center);
|
||||
ResetTransformOrigin();
|
||||
UpdateTransform();
|
||||
|
||||
if (centerAnimation == null)
|
||||
|
|
@ -616,8 +635,6 @@ namespace MapControl
|
|||
InternalSetValue(CenterProperty, TargetCenter);
|
||||
InternalSetValue(CenterPointProperty, mapTransform.Transform(TargetCenter));
|
||||
RemoveAnimation(CenterPointProperty); // remove holding animation in WPF
|
||||
|
||||
ResetTransformOrigin();
|
||||
UpdateTransform();
|
||||
}
|
||||
}
|
||||
|
|
@ -628,7 +645,6 @@ namespace MapControl
|
|||
{
|
||||
centerPoint.X = Location.NormalizeLongitude(centerPoint.X);
|
||||
InternalSetValue(CenterProperty, mapTransform.Transform(centerPoint));
|
||||
ResetTransformOrigin();
|
||||
UpdateTransform();
|
||||
}
|
||||
}
|
||||
|
|
@ -720,7 +736,7 @@ namespace MapControl
|
|||
InternalSetValue(ZoomLevelProperty, TargetZoomLevel);
|
||||
RemoveAnimation(ZoomLevelProperty); // remove holding animation in WPF
|
||||
|
||||
UpdateTransform(true);
|
||||
Dispatcher.BeginInvoke(() => UpdateTransform(true));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -793,12 +809,11 @@ namespace MapControl
|
|||
|
||||
InternalSetValue(HeadingProperty, TargetHeading);
|
||||
RemoveAnimation(HeadingProperty); // remove holding animation in WPF
|
||||
|
||||
UpdateTransform();
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateTransform(bool resetTransformOrigin = false)
|
||||
private void UpdateTransform(bool resetOrigin = false)
|
||||
{
|
||||
var center = transformOrigin ?? Center;
|
||||
|
||||
|
|
@ -812,7 +827,7 @@ namespace MapControl
|
|||
if (center.Latitude < -mapTransform.MaxLatitude || center.Latitude > mapTransform.MaxLatitude)
|
||||
{
|
||||
center.Latitude = Math.Min(Math.Max(center.Latitude, -mapTransform.MaxLatitude), mapTransform.MaxLatitude);
|
||||
resetTransformOrigin = true;
|
||||
resetOrigin = true;
|
||||
}
|
||||
|
||||
InternalSetValue(CenterProperty, center);
|
||||
|
|
@ -823,7 +838,7 @@ namespace MapControl
|
|||
InternalSetValue(CenterPointProperty, mapTransform.Transform(center));
|
||||
}
|
||||
|
||||
if (resetTransformOrigin)
|
||||
if (resetOrigin)
|
||||
{
|
||||
ResetTransformOrigin();
|
||||
SetViewportTransform(center);
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
#if NETFX_CORE
|
||||
using System;
|
||||
using Windows.Foundation;
|
||||
#else
|
||||
using System.Windows;
|
||||
|
|
@ -25,21 +26,21 @@ namespace MapControl
|
|||
public abstract double MaxLatitude { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the scale factor of the map projection at the specified geographic location
|
||||
/// relative to the scale at latitude zero.
|
||||
/// Gets the scale factor of the map projection at the specified
|
||||
/// geographic location relative to the scale at latitude zero.
|
||||
/// </summary>
|
||||
public abstract double RelativeScale(Location location);
|
||||
|
||||
/// <summary>
|
||||
/// Transforms a cartesian coordinate point to a geographic location.
|
||||
/// </summary>
|
||||
public abstract Location Transform(Point point);
|
||||
|
||||
/// <summary>
|
||||
/// Transforms a geographic location to a cartesian coordinate point.
|
||||
/// </summary>
|
||||
public abstract Point Transform(Location location);
|
||||
|
||||
/// <summary>
|
||||
/// Transforms a cartesian coordinate point to a geographic location.
|
||||
/// </summary>
|
||||
public abstract Location Transform(Point point);
|
||||
|
||||
/// <summary>
|
||||
/// Transforms a geographic location to a cartesian coordinate point
|
||||
/// with minumum distance to the specified reference longitude value.
|
||||
|
|
@ -61,5 +62,22 @@ namespace MapControl
|
|||
|
||||
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>
|
||||
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)
|
||||
{
|
||||
|
|
@ -46,13 +46,12 @@ namespace MapControl
|
|||
return double.PositiveInfinity;
|
||||
}
|
||||
|
||||
latitude *= Math.PI / 180d;
|
||||
return Math.Log(Math.Tan(latitude) + 1d / Math.Cos(latitude)) / Math.PI * 180d;
|
||||
return Math.Log(Math.Tan((latitude + 90d) * Math.PI / 360d)) / Math.PI * 180d;
|
||||
}
|
||||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ namespace MapControl
|
|||
new Matrix(1d, 0d, 0d, 1d, TileSource.TileSize * TileGrid.XMin, TileSource.TileSize * TileGrid.YMin)
|
||||
.Scale(scale, scale)
|
||||
.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