2012-11-22 21:42:29 +01:00
|
|
|
|
// XAML Map Control - http://xamlmapcontrol.codeplex.com/
|
2013-05-07 18:12:25 +02:00
|
|
|
|
// Copyright © Clemens Fischer 2012-2013
|
2012-10-12 19:22:49 +02:00
|
|
|
|
// Licensed under the Microsoft Public License (Ms-PL)
|
|
|
|
|
|
|
|
|
|
|
|
using System;
|
|
|
|
|
|
using System.Collections.Specialized;
|
|
|
|
|
|
using System.Linq;
|
2012-12-09 22:18:31 +01:00
|
|
|
|
#if NETFX_CORE
|
2012-11-22 21:42:29 +01:00
|
|
|
|
using Windows.Foundation;
|
|
|
|
|
|
using Windows.UI.Xaml;
|
|
|
|
|
|
using Windows.UI.Xaml.Media;
|
|
|
|
|
|
using Windows.UI.Xaml.Media.Animation;
|
|
|
|
|
|
#else
|
2012-10-12 19:22:49 +02:00
|
|
|
|
using System.Windows;
|
|
|
|
|
|
using System.Windows.Media;
|
|
|
|
|
|
using System.Windows.Media.Animation;
|
2012-11-22 21:42:29 +01:00
|
|
|
|
#endif
|
2012-10-12 19:22:49 +02:00
|
|
|
|
|
|
|
|
|
|
namespace MapControl
|
|
|
|
|
|
{
|
|
|
|
|
|
/// <summary>
|
2012-11-22 21:42:29 +01:00
|
|
|
|
/// The map control. Draws map content provided by the TileLayers or the TileLayer property.
|
2012-10-12 19:22:49 +02:00
|
|
|
|
/// The visible map area is defined by the Center and ZoomLevel properties. The map can be rotated
|
|
|
|
|
|
/// by an angle that is given by the Heading property.
|
|
|
|
|
|
/// MapBase is a MapPanel and hence can contain map overlays like other MapPanels or MapItemsControls.
|
|
|
|
|
|
/// </summary>
|
2012-11-22 21:42:29 +01:00
|
|
|
|
public partial class MapBase : MapPanel
|
2012-10-12 19:22:49 +02:00
|
|
|
|
{
|
2012-11-22 21:42:29 +01:00
|
|
|
|
public static TimeSpan AnimationDuration = TimeSpan.FromSeconds(0.5);
|
|
|
|
|
|
public static EasingFunctionBase AnimationEasingFunction = new QuadraticEase { EasingMode = EasingMode.EaseOut };
|
2012-10-12 19:22:49 +02:00
|
|
|
|
|
|
|
|
|
|
public static readonly DependencyProperty TileLayersProperty = DependencyProperty.Register(
|
2012-11-22 21:42:29 +01:00
|
|
|
|
"TileLayers", typeof(TileLayerCollection), typeof(MapBase), new PropertyMetadata(null,
|
|
|
|
|
|
(o, e) => ((MapBase)o).TileLayersPropertyChanged((TileLayerCollection)e.OldValue, (TileLayerCollection)e.NewValue)));
|
2012-10-12 19:22:49 +02:00
|
|
|
|
|
2012-11-22 21:42:29 +01:00
|
|
|
|
public static readonly DependencyProperty TileLayerProperty = DependencyProperty.Register(
|
|
|
|
|
|
"TileLayer", typeof(TileLayer), typeof(MapBase), new PropertyMetadata(null,
|
|
|
|
|
|
(o, e) => ((MapBase)o).TileLayerPropertyChanged((TileLayer)e.NewValue)));
|
2012-10-12 19:22:49 +02:00
|
|
|
|
|
|
|
|
|
|
public static readonly DependencyProperty TileOpacityProperty = DependencyProperty.Register(
|
2012-11-22 21:42:29 +01:00
|
|
|
|
"TileOpacity", typeof(double), typeof(MapBase), new PropertyMetadata(1d,
|
2012-10-12 19:22:49 +02:00
|
|
|
|
(o, e) => ((MapBase)o).tileContainer.Opacity = (double)e.NewValue));
|
|
|
|
|
|
|
|
|
|
|
|
public static readonly DependencyProperty CenterProperty = DependencyProperty.Register(
|
2012-11-22 21:42:29 +01:00
|
|
|
|
"Center", typeof(Location), typeof(MapBase), new PropertyMetadata(new Location(),
|
|
|
|
|
|
(o, e) => ((MapBase)o).CenterPropertyChanged((Location)e.NewValue)));
|
2012-10-12 19:22:49 +02:00
|
|
|
|
|
|
|
|
|
|
public static readonly DependencyProperty TargetCenterProperty = DependencyProperty.Register(
|
2012-11-22 21:42:29 +01:00
|
|
|
|
"TargetCenter", typeof(Location), typeof(MapBase), new PropertyMetadata(new Location(),
|
|
|
|
|
|
(o, e) => ((MapBase)o).TargetCenterPropertyChanged((Location)e.NewValue)));
|
|
|
|
|
|
|
|
|
|
|
|
internal static readonly DependencyProperty CenterPointProperty = DependencyProperty.Register(
|
|
|
|
|
|
"CenterPoint", typeof(Point), typeof(MapBase), new PropertyMetadata(new Point(),
|
|
|
|
|
|
(o, e) => ((MapBase)o).CenterPointPropertyChanged((Point)e.NewValue)));
|
|
|
|
|
|
|
|
|
|
|
|
public static readonly DependencyProperty MinZoomLevelProperty = DependencyProperty.Register(
|
|
|
|
|
|
"MinZoomLevel", typeof(double), typeof(MapBase), new PropertyMetadata(1d,
|
|
|
|
|
|
(o, e) => ((MapBase)o).MinZoomLevelPropertyChanged((double)e.NewValue)));
|
|
|
|
|
|
|
|
|
|
|
|
public static readonly DependencyProperty MaxZoomLevelProperty = DependencyProperty.Register(
|
|
|
|
|
|
"MaxZoomLevel", typeof(double), typeof(MapBase), new PropertyMetadata(18d,
|
|
|
|
|
|
(o, e) => ((MapBase)o).MaxZoomLevelPropertyChanged((double)e.NewValue)));
|
2012-10-12 19:22:49 +02:00
|
|
|
|
|
|
|
|
|
|
public static readonly DependencyProperty ZoomLevelProperty = DependencyProperty.Register(
|
2012-11-22 21:42:29 +01:00
|
|
|
|
"ZoomLevel", typeof(double), typeof(MapBase), new PropertyMetadata(1d,
|
|
|
|
|
|
(o, e) => ((MapBase)o).ZoomLevelPropertyChanged((double)e.NewValue)));
|
2012-10-12 19:22:49 +02:00
|
|
|
|
|
|
|
|
|
|
public static readonly DependencyProperty TargetZoomLevelProperty = DependencyProperty.Register(
|
2012-11-22 21:42:29 +01:00
|
|
|
|
"TargetZoomLevel", typeof(double), typeof(MapBase), new PropertyMetadata(1d,
|
|
|
|
|
|
(o, e) => ((MapBase)o).TargetZoomLevelPropertyChanged((double)e.NewValue)));
|
2012-10-12 19:22:49 +02:00
|
|
|
|
|
|
|
|
|
|
public static readonly DependencyProperty HeadingProperty = DependencyProperty.Register(
|
2012-11-22 21:42:29 +01:00
|
|
|
|
"Heading", typeof(double), typeof(MapBase), new PropertyMetadata(0d,
|
|
|
|
|
|
(o, e) => ((MapBase)o).HeadingPropertyChanged((double)e.NewValue)));
|
2012-10-12 19:22:49 +02:00
|
|
|
|
|
|
|
|
|
|
public static readonly DependencyProperty TargetHeadingProperty = DependencyProperty.Register(
|
2012-11-22 21:42:29 +01:00
|
|
|
|
"TargetHeading", typeof(double), typeof(MapBase), new PropertyMetadata(0d,
|
|
|
|
|
|
(o, e) => ((MapBase)o).TargetHeadingPropertyChanged((double)e.NewValue)));
|
2012-10-12 19:22:49 +02:00
|
|
|
|
|
2012-11-22 21:42:29 +01:00
|
|
|
|
public static readonly DependencyProperty CenterScaleProperty = DependencyProperty.Register(
|
2012-10-12 19:22:49 +02:00
|
|
|
|
"CenterScale", typeof(double), typeof(MapBase), null);
|
|
|
|
|
|
|
|
|
|
|
|
private readonly TileContainer tileContainer = new TileContainer();
|
|
|
|
|
|
private readonly MapTransform mapTransform = new MercatorTransform();
|
2012-11-22 21:42:29 +01:00
|
|
|
|
private readonly MatrixTransform scaleTransform = new MatrixTransform();
|
|
|
|
|
|
private readonly MatrixTransform rotateTransform = new MatrixTransform();
|
2012-10-12 19:22:49 +02:00
|
|
|
|
private readonly MatrixTransform scaleRotateTransform = new MatrixTransform();
|
|
|
|
|
|
private Location transformOrigin;
|
|
|
|
|
|
private Point viewportOrigin;
|
2012-11-22 21:42:29 +01:00
|
|
|
|
private PointAnimation centerAnimation;
|
2012-10-12 19:22:49 +02:00
|
|
|
|
private DoubleAnimation zoomLevelAnimation;
|
|
|
|
|
|
private DoubleAnimation headingAnimation;
|
2013-05-13 23:49:48 +02:00
|
|
|
|
private Brush storedBackground;
|
|
|
|
|
|
private Brush storedForeground;
|
2012-11-22 21:42:29 +01:00
|
|
|
|
private bool internalPropertyChange;
|
2012-10-12 19:22:49 +02:00
|
|
|
|
|
|
|
|
|
|
public MapBase()
|
|
|
|
|
|
{
|
2013-04-12 19:59:16 +02:00
|
|
|
|
SetParentMap();
|
2012-11-23 16:16:09 +01:00
|
|
|
|
|
2012-10-12 19:22:49 +02:00
|
|
|
|
TileLayers = new TileLayerCollection();
|
2012-11-22 21:42:29 +01:00
|
|
|
|
Initialize();
|
2012-10-12 19:22:49 +02:00
|
|
|
|
|
2012-11-22 21:42:29 +01:00
|
|
|
|
Loaded += OnLoaded;
|
2012-10-12 19:22:49 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2012-11-22 21:42:29 +01:00
|
|
|
|
partial void Initialize();
|
|
|
|
|
|
|
2012-10-12 19:22:49 +02:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Raised when the current viewport has changed.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public event EventHandler ViewportChanged;
|
|
|
|
|
|
|
2012-11-26 19:17:12 +01:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Gets or sets the map foreground Brush.
|
|
|
|
|
|
/// </summary>
|
2012-10-12 19:22:49 +02:00
|
|
|
|
public Brush Foreground
|
|
|
|
|
|
{
|
|
|
|
|
|
get { return (Brush)GetValue(ForegroundProperty); }
|
|
|
|
|
|
set { SetValue(ForegroundProperty, value); }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Gets or sets the TileLayers used by this Map.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public TileLayerCollection TileLayers
|
|
|
|
|
|
{
|
|
|
|
|
|
get { return (TileLayerCollection)GetValue(TileLayersProperty); }
|
|
|
|
|
|
set { SetValue(TileLayersProperty, value); }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Gets or sets the base TileLayer used by this Map, i.e. TileLayers[0].
|
|
|
|
|
|
/// </summary>
|
2012-11-22 21:42:29 +01:00
|
|
|
|
public TileLayer TileLayer
|
2012-10-12 19:22:49 +02:00
|
|
|
|
{
|
2012-11-22 21:42:29 +01:00
|
|
|
|
get { return (TileLayer)GetValue(TileLayerProperty); }
|
|
|
|
|
|
set { SetValue(TileLayerProperty, value); }
|
2012-10-12 19:22:49 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Gets or sets the opacity of the tile layers.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public double TileOpacity
|
|
|
|
|
|
{
|
|
|
|
|
|
get { return (double)GetValue(TileOpacityProperty); }
|
|
|
|
|
|
set { SetValue(TileOpacityProperty, value); }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Gets or sets the location of the center point of the Map.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public Location Center
|
|
|
|
|
|
{
|
|
|
|
|
|
get { return (Location)GetValue(CenterProperty); }
|
|
|
|
|
|
set { SetValue(CenterProperty, value); }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Gets or sets the target value of a Center animation.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public Location TargetCenter
|
|
|
|
|
|
{
|
|
|
|
|
|
get { return (Location)GetValue(TargetCenterProperty); }
|
|
|
|
|
|
set { SetValue(TargetCenterProperty, value); }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2012-11-22 21:42:29 +01:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Gets or sets the minimum value of the ZoomLevel and TargetZommLevel properties.
|
|
|
|
|
|
/// Must be greater than or equal to zero and less than or equal to MaxZoomLevel.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public double MinZoomLevel
|
|
|
|
|
|
{
|
|
|
|
|
|
get { return (double)GetValue(MinZoomLevelProperty); }
|
|
|
|
|
|
set { SetValue(MinZoomLevelProperty, value); }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Gets or sets the maximum value of the ZoomLevel and TargetZommLevel properties.
|
|
|
|
|
|
/// Must be greater than or equal to MinZoomLevel and less than or equal to 20.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public double MaxZoomLevel
|
|
|
|
|
|
{
|
|
|
|
|
|
get { return (double)GetValue(MaxZoomLevelProperty); }
|
|
|
|
|
|
set { SetValue(MaxZoomLevelProperty, value); }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2012-10-12 19:22:49 +02:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Gets or sets the map zoom level.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public double ZoomLevel
|
|
|
|
|
|
{
|
|
|
|
|
|
get { return (double)GetValue(ZoomLevelProperty); }
|
|
|
|
|
|
set { SetValue(ZoomLevelProperty, value); }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Gets or sets the target value of a ZoomLevel animation.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public double TargetZoomLevel
|
|
|
|
|
|
{
|
|
|
|
|
|
get { return (double)GetValue(TargetZoomLevelProperty); }
|
|
|
|
|
|
set { SetValue(TargetZoomLevelProperty, value); }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Gets or sets the map heading, or clockwise rotation angle in degrees.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public double Heading
|
|
|
|
|
|
{
|
|
|
|
|
|
get { return (double)GetValue(HeadingProperty); }
|
|
|
|
|
|
set { SetValue(HeadingProperty, value); }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Gets or sets the target value of a Heading animation.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public double TargetHeading
|
|
|
|
|
|
{
|
|
|
|
|
|
get { return (double)GetValue(TargetHeadingProperty); }
|
|
|
|
|
|
set { SetValue(TargetHeadingProperty, value); }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Gets the map scale at the Center location as viewport coordinate units (pixels) per meter.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public double CenterScale
|
|
|
|
|
|
{
|
|
|
|
|
|
get { return (double)GetValue(CenterScaleProperty); }
|
2012-11-22 21:42:29 +01:00
|
|
|
|
private set { SetValue(CenterScaleProperty, value); }
|
2012-10-12 19:22:49 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Gets the transformation from geographic coordinates to cartesian map coordinates.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public MapTransform MapTransform
|
|
|
|
|
|
{
|
|
|
|
|
|
get { return mapTransform; }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Gets the transformation from cartesian map coordinates to viewport coordinates.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public Transform ViewportTransform
|
|
|
|
|
|
{
|
|
|
|
|
|
get { return tileContainer.ViewportTransform; }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Gets the scaling transformation from meters to viewport coordinate units (pixels)
|
|
|
|
|
|
/// at the viewport center point.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public Transform ScaleTransform
|
|
|
|
|
|
{
|
|
|
|
|
|
get { return scaleTransform; }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Gets the transformation that rotates by the value of the Heading property.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public Transform RotateTransform
|
|
|
|
|
|
{
|
|
|
|
|
|
get { return rotateTransform; }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Gets the combination of ScaleTransform and RotateTransform
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public Transform ScaleRotateTransform
|
|
|
|
|
|
{
|
|
|
|
|
|
get { return scaleRotateTransform; }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2013-05-07 18:12:25 +02:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Gets the conversion factor from longitude degrees to meters, at latitude = 0.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public double MetersPerDegree
|
|
|
|
|
|
{
|
|
|
|
|
|
get
|
|
|
|
|
|
{
|
|
|
|
|
|
return (TileLayer != null && TileLayer.TileSource != null) ?
|
|
|
|
|
|
TileLayer.TileSource.MetersPerDegree : (TileSource.EarthRadius * Math.PI / 180d);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2012-11-22 21:42:29 +01:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Gets the map scale at the specified location as viewport coordinate units (pixels) per meter.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public double GetMapScale(Location location)
|
|
|
|
|
|
{
|
2013-05-07 18:12:25 +02:00
|
|
|
|
return mapTransform.RelativeScale(location) * Math.Pow(2d, ZoomLevel) * TileSource.TileSize / (MetersPerDegree * 360d);
|
2012-11-22 21:42:29 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2012-10-12 19:22:49 +02:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Transforms a geographic location to a viewport coordinates point.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public Point LocationToViewportPoint(Location location)
|
|
|
|
|
|
{
|
2012-11-22 21:42:29 +01:00
|
|
|
|
return ViewportTransform.Transform(mapTransform.Transform(location));
|
2012-10-12 19:22:49 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Transforms a viewport coordinates point to a geographic location.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public Location ViewportPointToLocation(Point point)
|
|
|
|
|
|
{
|
2012-11-22 21:42:29 +01:00
|
|
|
|
return mapTransform.Transform(ViewportTransform.Inverse.Transform(point));
|
2012-10-12 19:22:49 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Sets a temporary origin location in geographic coordinates for scaling and rotation transformations.
|
|
|
|
|
|
/// This origin location is automatically removed when the Center property is set by application code.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public void SetTransformOrigin(Location origin)
|
|
|
|
|
|
{
|
|
|
|
|
|
transformOrigin = origin;
|
|
|
|
|
|
viewportOrigin = LocationToViewportPoint(origin);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Sets a temporary origin point in viewport coordinates for scaling and rotation transformations.
|
|
|
|
|
|
/// This origin point is automatically removed when the Center property is set by application code.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public void SetTransformOrigin(Point origin)
|
|
|
|
|
|
{
|
|
|
|
|
|
viewportOrigin.X = Math.Min(Math.Max(origin.X, 0d), RenderSize.Width);
|
|
|
|
|
|
viewportOrigin.Y = Math.Min(Math.Max(origin.Y, 0d), RenderSize.Height);
|
2013-04-04 18:01:13 +02:00
|
|
|
|
transformOrigin = ViewportPointToLocation(viewportOrigin);
|
2012-10-12 19:22:49 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Removes the temporary transform origin point set by SetTransformOrigin.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public void ResetTransformOrigin()
|
|
|
|
|
|
{
|
|
|
|
|
|
transformOrigin = null;
|
|
|
|
|
|
viewportOrigin = new Point(RenderSize.Width / 2d, RenderSize.Height / 2d);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Changes the Center property according to the specified translation in viewport coordinates.
|
|
|
|
|
|
/// </summary>
|
2012-11-22 21:42:29 +01:00
|
|
|
|
public void TranslateMap(Point translation)
|
2012-10-12 19:22:49 +02:00
|
|
|
|
{
|
|
|
|
|
|
if (translation.X != 0d || translation.Y != 0d)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (transformOrigin != null)
|
|
|
|
|
|
{
|
2012-11-22 21:42:29 +01:00
|
|
|
|
viewportOrigin.X += translation.X;
|
|
|
|
|
|
viewportOrigin.Y += translation.Y;
|
2012-10-12 19:22:49 +02:00
|
|
|
|
UpdateTransform();
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2012-11-22 21:42:29 +01:00
|
|
|
|
Center = ViewportPointToLocation(new Point(viewportOrigin.X - translation.X, viewportOrigin.Y - translation.Y));
|
2012-10-12 19:22:49 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <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>
|
2012-11-22 21:42:29 +01:00
|
|
|
|
public void TransformMap(Point origin, Point translation, double rotation, double scale)
|
2012-10-12 19:22:49 +02:00
|
|
|
|
{
|
2013-04-04 18:01:13 +02:00
|
|
|
|
SetTransformOrigin(origin);
|
|
|
|
|
|
|
|
|
|
|
|
viewportOrigin.X += translation.X;
|
|
|
|
|
|
viewportOrigin.Y += translation.Y;
|
|
|
|
|
|
|
|
|
|
|
|
if (rotation != 0d)
|
2012-10-12 19:22:49 +02:00
|
|
|
|
{
|
2013-04-04 18:01:13 +02:00
|
|
|
|
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);
|
2012-10-12 19:22:49 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2013-04-04 18:01:13 +02:00
|
|
|
|
UpdateTransform();
|
2012-10-12 19:22:49 +02:00
|
|
|
|
ResetTransformOrigin();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2013-06-07 15:09:59 +02:00
|
|
|
|
/// Sets the value of the TargetZoomLevel property while retaining the specified origin point
|
2012-10-12 19:22:49 +02:00
|
|
|
|
/// in viewport coordinates.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public void ZoomMap(Point origin, double zoomLevel)
|
|
|
|
|
|
{
|
2013-06-07 15:09:59 +02:00
|
|
|
|
SetTransformOrigin(origin);
|
|
|
|
|
|
|
2013-04-04 18:01:13 +02:00
|
|
|
|
var targetZoomLevel = TargetZoomLevel;
|
2012-10-12 19:22:49 +02:00
|
|
|
|
TargetZoomLevel = zoomLevel;
|
|
|
|
|
|
|
2013-06-07 15:09:59 +02:00
|
|
|
|
if (TargetZoomLevel == targetZoomLevel) // TargetZoomLevel might be coerced
|
2012-10-12 19:22:49 +02:00
|
|
|
|
{
|
2013-06-07 15:09:59 +02:00
|
|
|
|
ResetTransformOrigin();
|
2012-10-12 19:22:49 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2012-11-22 21:42:29 +01:00
|
|
|
|
protected override void OnViewportChanged()
|
2012-10-12 19:22:49 +02:00
|
|
|
|
{
|
2012-11-22 21:42:29 +01:00
|
|
|
|
base.OnViewportChanged();
|
2012-10-12 19:22:49 +02:00
|
|
|
|
|
2012-11-22 21:42:29 +01:00
|
|
|
|
if (ViewportChanged != null)
|
2012-10-12 19:22:49 +02:00
|
|
|
|
{
|
2012-11-22 21:42:29 +01:00
|
|
|
|
ViewportChanged(this, EventArgs.Empty);
|
2012-10-12 19:22:49 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2012-11-22 21:42:29 +01:00
|
|
|
|
private void OnLoaded(object sender, RoutedEventArgs e)
|
2012-10-12 19:22:49 +02:00
|
|
|
|
{
|
2012-11-22 21:42:29 +01:00
|
|
|
|
Loaded -= OnLoaded;
|
2012-10-12 19:22:49 +02:00
|
|
|
|
|
2013-05-17 07:52:20 +02:00
|
|
|
|
if (TileLayer == null)
|
2012-10-12 19:22:49 +02:00
|
|
|
|
{
|
2012-11-22 21:42:29 +01:00
|
|
|
|
TileLayer = TileLayer.Default;
|
2012-10-12 19:22:49 +02:00
|
|
|
|
}
|
2012-11-22 21:42:29 +01:00
|
|
|
|
|
|
|
|
|
|
UpdateTransform();
|
2012-10-12 19:22:49 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void TileLayerCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
|
|
|
|
|
|
{
|
|
|
|
|
|
switch (e.Action)
|
|
|
|
|
|
{
|
|
|
|
|
|
case NotifyCollectionChangedAction.Add:
|
|
|
|
|
|
tileContainer.AddTileLayers(e.NewStartingIndex, e.NewItems.Cast<TileLayer>());
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case NotifyCollectionChangedAction.Remove:
|
2012-11-22 21:42:29 +01:00
|
|
|
|
tileContainer.RemoveTileLayers(e.OldStartingIndex, e.OldItems.Count);
|
2012-10-12 19:22:49 +02:00
|
|
|
|
break;
|
2012-11-22 21:42:29 +01:00
|
|
|
|
#if !SILVERLIGHT
|
2012-10-12 19:22:49 +02:00
|
|
|
|
case NotifyCollectionChangedAction.Move:
|
2012-11-22 21:42:29 +01:00
|
|
|
|
#endif
|
2012-10-12 19:22:49 +02:00
|
|
|
|
case NotifyCollectionChangedAction.Replace:
|
2012-11-22 21:42:29 +01:00
|
|
|
|
tileContainer.RemoveTileLayers(e.NewStartingIndex, e.OldItems.Count);
|
2012-10-12 19:22:49 +02:00
|
|
|
|
tileContainer.AddTileLayers(e.NewStartingIndex, e.NewItems.Cast<TileLayer>());
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case NotifyCollectionChangedAction.Reset:
|
|
|
|
|
|
tileContainer.ClearTileLayers();
|
|
|
|
|
|
if (e.NewItems != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
tileContainer.AddTileLayers(0, e.NewItems.Cast<TileLayer>());
|
|
|
|
|
|
}
|
|
|
|
|
|
break;
|
2012-11-22 21:42:29 +01:00
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
|
break;
|
2012-10-12 19:22:49 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2012-11-22 21:42:29 +01:00
|
|
|
|
UpdateTileLayer();
|
2012-10-12 19:22:49 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void TileLayersPropertyChanged(TileLayerCollection oldTileLayers, TileLayerCollection newTileLayers)
|
|
|
|
|
|
{
|
|
|
|
|
|
tileContainer.ClearTileLayers();
|
|
|
|
|
|
|
|
|
|
|
|
if (oldTileLayers != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
oldTileLayers.CollectionChanged -= TileLayerCollectionChanged;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (newTileLayers != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
newTileLayers.CollectionChanged += TileLayerCollectionChanged;
|
|
|
|
|
|
tileContainer.AddTileLayers(0, newTileLayers);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2012-11-22 21:42:29 +01:00
|
|
|
|
UpdateTileLayer();
|
2012-10-12 19:22:49 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2012-11-22 21:42:29 +01:00
|
|
|
|
private void TileLayerPropertyChanged(TileLayer tileLayer)
|
2012-10-12 19:22:49 +02:00
|
|
|
|
{
|
2012-11-22 21:42:29 +01:00
|
|
|
|
if (tileLayer != null)
|
2012-10-12 19:22:49 +02:00
|
|
|
|
{
|
2012-11-22 21:42:29 +01:00
|
|
|
|
if (TileLayers == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
TileLayers = new TileLayerCollection();
|
|
|
|
|
|
}
|
2012-10-12 19:22:49 +02:00
|
|
|
|
|
|
|
|
|
|
if (TileLayers.Count == 0)
|
|
|
|
|
|
{
|
2012-11-22 21:42:29 +01:00
|
|
|
|
TileLayers.Add(tileLayer);
|
2012-10-12 19:22:49 +02:00
|
|
|
|
}
|
2012-11-22 21:42:29 +01:00
|
|
|
|
else if (TileLayers[0] != tileLayer)
|
2012-10-12 19:22:49 +02:00
|
|
|
|
{
|
2012-11-22 21:42:29 +01:00
|
|
|
|
TileLayers[0] = tileLayer;
|
2012-10-12 19:22:49 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2013-04-21 23:56:08 +02:00
|
|
|
|
if (tileLayer != null && tileLayer.Background != null)
|
2012-10-12 19:22:49 +02:00
|
|
|
|
{
|
2013-05-13 23:49:48 +02:00
|
|
|
|
if (storedBackground == null)
|
2012-10-12 19:22:49 +02:00
|
|
|
|
{
|
2013-05-13 23:49:48 +02:00
|
|
|
|
storedBackground = Background;
|
2012-10-12 19:22:49 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2013-04-21 23:56:08 +02:00
|
|
|
|
Background = tileLayer.Background;
|
2012-10-12 19:22:49 +02:00
|
|
|
|
}
|
2013-05-13 23:49:48 +02:00
|
|
|
|
else if (storedBackground != null)
|
2012-10-12 19:22:49 +02:00
|
|
|
|
{
|
2013-05-13 23:49:48 +02:00
|
|
|
|
Background = storedBackground;
|
|
|
|
|
|
storedBackground = null;
|
2013-04-21 23:56:08 +02:00
|
|
|
|
}
|
2012-10-12 19:22:49 +02:00
|
|
|
|
|
2013-04-21 23:56:08 +02:00
|
|
|
|
if (tileLayer != null && tileLayer.Foreground != null)
|
|
|
|
|
|
{
|
2013-05-13 23:49:48 +02:00
|
|
|
|
if (storedForeground == null)
|
2012-10-12 19:22:49 +02:00
|
|
|
|
{
|
2013-05-13 23:49:48 +02:00
|
|
|
|
storedForeground = Foreground;
|
2012-10-12 19:22:49 +02:00
|
|
|
|
}
|
2013-04-21 23:56:08 +02:00
|
|
|
|
|
|
|
|
|
|
Foreground = tileLayer.Foreground;
|
|
|
|
|
|
}
|
2013-05-13 23:49:48 +02:00
|
|
|
|
else if (storedForeground != null)
|
2013-04-21 23:56:08 +02:00
|
|
|
|
{
|
2013-05-13 23:49:48 +02:00
|
|
|
|
Foreground = storedForeground;
|
|
|
|
|
|
storedForeground = null;
|
2012-10-12 19:22:49 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2012-11-22 21:42:29 +01:00
|
|
|
|
private void UpdateTileLayer()
|
2012-10-12 19:22:49 +02:00
|
|
|
|
{
|
2013-05-17 07:52:20 +02:00
|
|
|
|
TileLayer tileLayer = null;
|
|
|
|
|
|
|
|
|
|
|
|
if (TileLayers != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
tileLayer = TileLayers.FirstOrDefault();
|
|
|
|
|
|
}
|
2012-11-22 21:42:29 +01:00
|
|
|
|
|
|
|
|
|
|
if (TileLayer != tileLayer)
|
2012-10-12 19:22:49 +02:00
|
|
|
|
{
|
2012-11-22 21:42:29 +01:00
|
|
|
|
TileLayer = tileLayer;
|
2012-10-12 19:22:49 +02:00
|
|
|
|
}
|
2012-11-22 21:42:29 +01:00
|
|
|
|
}
|
2012-10-12 19:22:49 +02:00
|
|
|
|
|
2013-04-04 18:01:13 +02:00
|
|
|
|
private void InternalSetValue(DependencyProperty property, object value)
|
2012-11-22 21:42:29 +01:00
|
|
|
|
{
|
|
|
|
|
|
internalPropertyChange = true;
|
|
|
|
|
|
SetValue(property, value);
|
|
|
|
|
|
internalPropertyChange = false;
|
2012-10-12 19:22:49 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2013-04-04 18:01:13 +02:00
|
|
|
|
private bool CoerceLocation(Location location, double latitudeEpsilon = 0d)
|
2012-10-12 19:22:49 +02:00
|
|
|
|
{
|
2013-04-04 18:01:13 +02:00
|
|
|
|
var maxLatitude = mapTransform.MaxLatitude + latitudeEpsilon;
|
|
|
|
|
|
var latitude = Math.Min(Math.Max(location.Latitude, -maxLatitude), maxLatitude);
|
|
|
|
|
|
var longitude = Location.NormalizeLongitude(location.Longitude);
|
|
|
|
|
|
|
|
|
|
|
|
if (location.Latitude != latitude || location.Longitude != longitude)
|
|
|
|
|
|
{
|
|
|
|
|
|
location.Latitude = latitude;
|
|
|
|
|
|
location.Longitude = longitude;
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return false;
|
2012-11-22 21:42:29 +01:00
|
|
|
|
}
|
2012-10-12 19:22:49 +02:00
|
|
|
|
|
2013-04-04 18:01:13 +02:00
|
|
|
|
private void CoerceCenterProperty(DependencyProperty property, Location center)
|
2012-11-22 21:42:29 +01:00
|
|
|
|
{
|
2013-04-04 18:01:13 +02:00
|
|
|
|
if (CoerceLocation(center))
|
2012-10-12 19:22:49 +02:00
|
|
|
|
{
|
2013-04-04 18:01:13 +02:00
|
|
|
|
InternalSetValue(property, center);
|
2012-10-12 19:22:49 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void CenterPropertyChanged(Location center)
|
|
|
|
|
|
{
|
2012-11-22 21:42:29 +01:00
|
|
|
|
if (!internalPropertyChange)
|
2012-10-12 19:22:49 +02:00
|
|
|
|
{
|
2013-04-04 18:01:13 +02:00
|
|
|
|
CoerceCenterProperty(CenterProperty, center);
|
2012-10-12 19:22:49 +02:00
|
|
|
|
ResetTransformOrigin();
|
|
|
|
|
|
UpdateTransform();
|
|
|
|
|
|
|
2012-11-22 21:42:29 +01:00
|
|
|
|
if (centerAnimation == null)
|
|
|
|
|
|
{
|
2013-04-04 18:01:13 +02:00
|
|
|
|
InternalSetValue(TargetCenterProperty, center);
|
|
|
|
|
|
InternalSetValue(CenterPointProperty, new Point(center.Longitude, center.Latitude));
|
2012-11-22 21:42:29 +01:00
|
|
|
|
}
|
2012-10-12 19:22:49 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void TargetCenterPropertyChanged(Location targetCenter)
|
|
|
|
|
|
{
|
2012-11-22 21:42:29 +01:00
|
|
|
|
if (!internalPropertyChange)
|
2012-10-12 19:22:49 +02:00
|
|
|
|
{
|
2013-04-04 18:01:13 +02:00
|
|
|
|
CoerceCenterProperty(TargetCenterProperty, targetCenter);
|
2012-11-22 21:42:29 +01:00
|
|
|
|
|
2013-04-04 18:01:13 +02:00
|
|
|
|
if (!targetCenter.Equals(Center))
|
2012-10-12 19:22:49 +02:00
|
|
|
|
{
|
2012-11-22 21:42:29 +01:00
|
|
|
|
if (centerAnimation != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
centerAnimation.Completed -= CenterAnimationCompleted;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// animate private CenterPoint property by PointAnimation
|
|
|
|
|
|
centerAnimation = new PointAnimation
|
|
|
|
|
|
{
|
|
|
|
|
|
From = new Point(Center.Longitude, Center.Latitude),
|
|
|
|
|
|
To = new Point(targetCenter.Longitude, targetCenter.Latitude),
|
|
|
|
|
|
Duration = AnimationDuration,
|
2013-05-07 18:12:25 +02:00
|
|
|
|
EasingFunction = AnimationEasingFunction,
|
2013-06-19 18:57:54 +02:00
|
|
|
|
FillBehavior = AnimationFillBehavior
|
2012-11-22 21:42:29 +01:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
centerAnimation.Completed += CenterAnimationCompleted;
|
|
|
|
|
|
this.BeginAnimation(CenterPointProperty, centerAnimation);
|
2012-10-12 19:22:49 +02:00
|
|
|
|
}
|
2012-11-22 21:42:29 +01:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2012-10-12 19:22:49 +02:00
|
|
|
|
|
2012-11-22 21:42:29 +01:00
|
|
|
|
private void CenterAnimationCompleted(object sender, object e)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (centerAnimation != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
centerAnimation.Completed -= CenterAnimationCompleted;
|
|
|
|
|
|
centerAnimation = null;
|
2012-10-12 19:22:49 +02:00
|
|
|
|
|
2013-04-04 18:01:13 +02:00
|
|
|
|
InternalSetValue(CenterProperty, TargetCenter);
|
|
|
|
|
|
InternalSetValue(CenterPointProperty, new Point(TargetCenter.Longitude, TargetCenter.Latitude));
|
2012-11-22 21:42:29 +01:00
|
|
|
|
ResetTransformOrigin();
|
|
|
|
|
|
UpdateTransform();
|
2012-10-12 19:22:49 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2012-11-22 21:42:29 +01:00
|
|
|
|
private void CenterPointPropertyChanged(Point centerPoint)
|
2012-10-12 19:22:49 +02:00
|
|
|
|
{
|
2012-11-22 21:42:29 +01:00
|
|
|
|
if (!internalPropertyChange)
|
|
|
|
|
|
{
|
2013-04-04 18:01:13 +02:00
|
|
|
|
InternalSetValue(CenterProperty, new Location(centerPoint.Y, centerPoint.X));
|
2012-11-22 21:42:29 +01:00
|
|
|
|
ResetTransformOrigin();
|
|
|
|
|
|
UpdateTransform();
|
|
|
|
|
|
}
|
2012-10-12 19:22:49 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2012-11-22 21:42:29 +01:00
|
|
|
|
private void MinZoomLevelPropertyChanged(double minZoomLevel)
|
2012-10-12 19:22:49 +02:00
|
|
|
|
{
|
2012-11-22 21:42:29 +01:00
|
|
|
|
var coercedValue = Math.Min(Math.Max(minZoomLevel, 0d), MaxZoomLevel);
|
|
|
|
|
|
|
|
|
|
|
|
if (coercedValue != minZoomLevel)
|
|
|
|
|
|
{
|
2013-04-04 18:01:13 +02:00
|
|
|
|
InternalSetValue(MinZoomLevelProperty, coercedValue);
|
2012-11-22 21:42:29 +01:00
|
|
|
|
}
|
|
|
|
|
|
else if (ZoomLevel < minZoomLevel)
|
|
|
|
|
|
{
|
|
|
|
|
|
ZoomLevel = minZoomLevel;
|
|
|
|
|
|
}
|
2012-10-12 19:22:49 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2012-11-22 21:42:29 +01:00
|
|
|
|
private void MaxZoomLevelPropertyChanged(double maxZoomLevel)
|
2012-10-12 19:22:49 +02:00
|
|
|
|
{
|
2012-12-15 18:54:40 +01:00
|
|
|
|
var coercedValue = Math.Min(Math.Max(maxZoomLevel, MinZoomLevel), 22d);
|
2012-11-22 21:42:29 +01:00
|
|
|
|
|
|
|
|
|
|
if (coercedValue != maxZoomLevel)
|
2012-10-12 19:22:49 +02:00
|
|
|
|
{
|
2013-04-04 18:01:13 +02:00
|
|
|
|
InternalSetValue(MaxZoomLevelProperty, coercedValue);
|
2012-11-22 21:42:29 +01:00
|
|
|
|
}
|
|
|
|
|
|
else if (ZoomLevel > maxZoomLevel)
|
|
|
|
|
|
{
|
|
|
|
|
|
ZoomLevel = maxZoomLevel;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2012-12-15 18:54:40 +01:00
|
|
|
|
private bool CoerceZoomLevelProperty(DependencyProperty property, ref double zoomLevel)
|
2012-11-22 21:42:29 +01:00
|
|
|
|
{
|
2013-04-04 18:01:13 +02:00
|
|
|
|
var coercedValue = Math.Min(Math.Max(zoomLevel, MinZoomLevel), MaxZoomLevel);
|
2012-11-22 21:42:29 +01:00
|
|
|
|
|
2012-12-15 18:54:40 +01:00
|
|
|
|
if (coercedValue != zoomLevel)
|
2012-11-22 21:42:29 +01:00
|
|
|
|
{
|
2013-04-04 18:01:13 +02:00
|
|
|
|
InternalSetValue(property, coercedValue);
|
|
|
|
|
|
return true;
|
2012-10-12 19:22:49 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2013-04-04 18:01:13 +02:00
|
|
|
|
return false;
|
2012-11-22 21:42:29 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void ZoomLevelPropertyChanged(double zoomLevel)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!internalPropertyChange &&
|
|
|
|
|
|
!CoerceZoomLevelProperty(ZoomLevelProperty, ref zoomLevel))
|
2012-10-12 19:22:49 +02:00
|
|
|
|
{
|
2012-11-22 21:42:29 +01:00
|
|
|
|
UpdateTransform();
|
|
|
|
|
|
|
|
|
|
|
|
if (zoomLevelAnimation == null)
|
|
|
|
|
|
{
|
2013-04-04 18:01:13 +02:00
|
|
|
|
InternalSetValue(TargetZoomLevelProperty, zoomLevel);
|
2012-11-22 21:42:29 +01:00
|
|
|
|
}
|
2012-10-12 19:22:49 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void TargetZoomLevelPropertyChanged(double targetZoomLevel)
|
|
|
|
|
|
{
|
2012-11-22 21:42:29 +01:00
|
|
|
|
if (!internalPropertyChange &&
|
|
|
|
|
|
!CoerceZoomLevelProperty(TargetZoomLevelProperty, ref targetZoomLevel) &&
|
|
|
|
|
|
targetZoomLevel != ZoomLevel)
|
2012-10-12 19:22:49 +02:00
|
|
|
|
{
|
|
|
|
|
|
if (zoomLevelAnimation != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
zoomLevelAnimation.Completed -= ZoomLevelAnimationCompleted;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
zoomLevelAnimation = new DoubleAnimation
|
|
|
|
|
|
{
|
|
|
|
|
|
To = targetZoomLevel,
|
2012-11-22 21:42:29 +01:00
|
|
|
|
Duration = AnimationDuration,
|
2013-05-07 18:12:25 +02:00
|
|
|
|
EasingFunction = AnimationEasingFunction,
|
2013-06-19 18:57:54 +02:00
|
|
|
|
FillBehavior = AnimationFillBehavior
|
2012-10-12 19:22:49 +02:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
zoomLevelAnimation.Completed += ZoomLevelAnimationCompleted;
|
2012-11-22 21:42:29 +01:00
|
|
|
|
this.BeginAnimation(ZoomLevelProperty, zoomLevelAnimation);
|
2012-10-12 19:22:49 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2012-11-22 21:42:29 +01:00
|
|
|
|
private void ZoomLevelAnimationCompleted(object sender, object e)
|
2012-10-12 19:22:49 +02:00
|
|
|
|
{
|
2012-11-22 21:42:29 +01:00
|
|
|
|
if (zoomLevelAnimation != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
zoomLevelAnimation.Completed -= ZoomLevelAnimationCompleted;
|
|
|
|
|
|
zoomLevelAnimation = null;
|
|
|
|
|
|
|
2013-04-04 18:01:13 +02:00
|
|
|
|
InternalSetValue(ZoomLevelProperty, TargetZoomLevel);
|
2012-11-22 21:42:29 +01:00
|
|
|
|
UpdateTransform();
|
|
|
|
|
|
ResetTransformOrigin();
|
|
|
|
|
|
}
|
2012-10-12 19:22:49 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2013-02-13 19:31:51 +01:00
|
|
|
|
private void CoerceHeadingProperty(DependencyProperty property, ref double heading)
|
2012-10-12 19:22:49 +02:00
|
|
|
|
{
|
2013-05-07 18:12:25 +02:00
|
|
|
|
var coercedValue = (heading >= -180d && heading <= 360d) ?
|
|
|
|
|
|
heading : (((heading % 360d) + 360d) % 360d);
|
2012-11-22 21:42:29 +01:00
|
|
|
|
|
2012-12-15 18:54:40 +01:00
|
|
|
|
if (coercedValue != heading)
|
2012-10-12 19:22:49 +02:00
|
|
|
|
{
|
2013-04-04 18:01:13 +02:00
|
|
|
|
InternalSetValue(property, coercedValue);
|
2012-10-12 19:22:49 +02:00
|
|
|
|
}
|
2012-11-22 21:42:29 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void HeadingPropertyChanged(double heading)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!internalPropertyChange)
|
2012-10-12 19:22:49 +02:00
|
|
|
|
{
|
2012-11-22 21:42:29 +01:00
|
|
|
|
CoerceHeadingProperty(HeadingProperty, ref heading);
|
|
|
|
|
|
UpdateTransform();
|
|
|
|
|
|
|
|
|
|
|
|
if (headingAnimation == null)
|
|
|
|
|
|
{
|
2013-04-04 18:01:13 +02:00
|
|
|
|
InternalSetValue(TargetHeadingProperty, heading);
|
2012-11-22 21:42:29 +01:00
|
|
|
|
}
|
2012-10-12 19:22:49 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void TargetHeadingPropertyChanged(double targetHeading)
|
|
|
|
|
|
{
|
2012-11-22 21:42:29 +01:00
|
|
|
|
if (!internalPropertyChange)
|
2012-10-12 19:22:49 +02:00
|
|
|
|
{
|
2012-11-22 21:42:29 +01:00
|
|
|
|
CoerceHeadingProperty(TargetHeadingProperty, ref targetHeading);
|
|
|
|
|
|
|
|
|
|
|
|
if (targetHeading != Heading)
|
2012-10-12 19:22:49 +02:00
|
|
|
|
{
|
2012-11-22 21:42:29 +01:00
|
|
|
|
var delta = targetHeading - Heading;
|
2012-10-12 19:22:49 +02:00
|
|
|
|
|
2012-11-22 21:42:29 +01:00
|
|
|
|
if (delta > 180d)
|
|
|
|
|
|
{
|
|
|
|
|
|
delta -= 360d;
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (delta < -180d)
|
|
|
|
|
|
{
|
|
|
|
|
|
delta += 360d;
|
|
|
|
|
|
}
|
2012-10-12 19:22:49 +02:00
|
|
|
|
|
2012-11-22 21:42:29 +01:00
|
|
|
|
if (headingAnimation != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
headingAnimation.Completed -= HeadingAnimationCompleted;
|
|
|
|
|
|
}
|
2012-10-12 19:22:49 +02:00
|
|
|
|
|
2012-11-22 21:42:29 +01:00
|
|
|
|
headingAnimation = new DoubleAnimation
|
|
|
|
|
|
{
|
|
|
|
|
|
By = delta,
|
|
|
|
|
|
Duration = AnimationDuration,
|
2013-05-07 18:12:25 +02:00
|
|
|
|
EasingFunction = AnimationEasingFunction,
|
2013-06-19 18:57:54 +02:00
|
|
|
|
FillBehavior = AnimationFillBehavior
|
2012-11-22 21:42:29 +01:00
|
|
|
|
};
|
2012-10-12 19:22:49 +02:00
|
|
|
|
|
2012-11-22 21:42:29 +01:00
|
|
|
|
headingAnimation.Completed += HeadingAnimationCompleted;
|
|
|
|
|
|
this.BeginAnimation(HeadingProperty, headingAnimation);
|
|
|
|
|
|
}
|
2012-10-12 19:22:49 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2012-11-22 21:42:29 +01:00
|
|
|
|
private void HeadingAnimationCompleted(object sender, object e)
|
2012-10-12 19:22:49 +02:00
|
|
|
|
{
|
2012-11-22 21:42:29 +01:00
|
|
|
|
if (headingAnimation != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
headingAnimation.Completed -= HeadingAnimationCompleted;
|
|
|
|
|
|
headingAnimation = null;
|
2012-10-12 19:22:49 +02:00
|
|
|
|
|
2013-04-04 18:01:13 +02:00
|
|
|
|
InternalSetValue(HeadingProperty, TargetHeading);
|
2012-11-22 21:42:29 +01:00
|
|
|
|
UpdateTransform();
|
|
|
|
|
|
}
|
2012-10-12 19:22:49 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void UpdateTransform()
|
|
|
|
|
|
{
|
2013-04-04 18:01:13 +02:00
|
|
|
|
var center = Center;
|
2013-05-13 23:49:48 +02:00
|
|
|
|
var scale = SetViewportTransform(transformOrigin ?? center);
|
2012-10-12 19:22:49 +02:00
|
|
|
|
|
|
|
|
|
|
if (transformOrigin != null)
|
|
|
|
|
|
{
|
2013-04-04 18:01:13 +02:00
|
|
|
|
center = ViewportPointToLocation(new Point(RenderSize.Width / 2d, RenderSize.Height / 2d));
|
2013-05-13 23:49:48 +02:00
|
|
|
|
|
2013-04-04 18:01:13 +02:00
|
|
|
|
var coerced = CoerceLocation(center, 1e-3);
|
|
|
|
|
|
|
|
|
|
|
|
InternalSetValue(CenterProperty, center);
|
|
|
|
|
|
|
|
|
|
|
|
if (coerced)
|
|
|
|
|
|
{
|
|
|
|
|
|
ResetTransformOrigin();
|
2013-05-13 23:49:48 +02:00
|
|
|
|
scale = SetViewportTransform(center);
|
2013-04-04 18:01:13 +02:00
|
|
|
|
}
|
2012-10-12 19:22:49 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2013-05-07 18:12:25 +02:00
|
|
|
|
scale *= mapTransform.RelativeScale(center) / MetersPerDegree; // Pixels per meter at center latitude
|
2012-10-12 19:22:49 +02:00
|
|
|
|
CenterScale = scale;
|
2012-11-22 21:42:29 +01:00
|
|
|
|
SetTransformMatrixes(scale);
|
2012-10-12 19:22:49 +02:00
|
|
|
|
|
|
|
|
|
|
OnViewportChanged();
|
|
|
|
|
|
}
|
2013-05-13 23:49:48 +02:00
|
|
|
|
|
|
|
|
|
|
private double SetViewportTransform(Location origin)
|
|
|
|
|
|
{
|
|
|
|
|
|
return tileContainer.SetViewportTransform(ZoomLevel, Heading, mapTransform.Transform(origin), viewportOrigin, RenderSize);
|
|
|
|
|
|
}
|
2012-10-12 19:22:49 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|