2012-11-22 21:42:29 +01:00
|
|
|
|
// XAML Map Control - http://xamlmapcontrol.codeplex.com/
|
2016-02-21 00:06:54 +01:00
|
|
|
|
// © 2016 Clemens Fischer
|
2012-10-12 19:22:49 +02:00
|
|
|
|
// Licensed under the Microsoft Public License (Ms-PL)
|
|
|
|
|
|
|
|
|
|
|
|
using System;
|
2014-11-19 21:11:14 +01:00
|
|
|
|
using System.Collections.Generic;
|
2014-12-17 21:38:28 +01:00
|
|
|
|
using System.Collections.ObjectModel;
|
2012-10-12 19:22:49 +02:00
|
|
|
|
using System.Collections.Specialized;
|
|
|
|
|
|
using System.Linq;
|
2015-08-09 20:04:44 +02: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>
|
2015-11-28 21:09:25 +01:00
|
|
|
|
/// The map control. Displays map content provided by the TileLayer or TileLayers property.
|
2015-08-09 20:04:44 +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 can contain map overlay child elements like other MapPanels or MapItemsControls.
|
2012-10-12 19:22:49 +02:00
|
|
|
|
/// </summary>
|
2012-11-22 21:42:29 +01:00
|
|
|
|
public partial class MapBase : MapPanel
|
2012-10-12 19:22:49 +02:00
|
|
|
|
{
|
2013-11-21 21:16:29 +01:00
|
|
|
|
private const double MaximumZoomLevel = 22d;
|
|
|
|
|
|
|
2012-11-22 21:42:29 +01:00
|
|
|
|
public static readonly DependencyProperty TileLayerProperty = DependencyProperty.Register(
|
2015-11-11 19:48:50 +01:00
|
|
|
|
"TileLayer", typeof(TileLayer), typeof(MapBase),
|
|
|
|
|
|
new PropertyMetadata(null, (o, e) => ((MapBase)o).TileLayerPropertyChanged((TileLayer)e.NewValue)));
|
2012-10-12 19:22:49 +02:00
|
|
|
|
|
2014-11-19 21:11:14 +01:00
|
|
|
|
public static readonly DependencyProperty TileLayersProperty = DependencyProperty.Register(
|
2015-11-11 19:48:50 +01:00
|
|
|
|
"TileLayers", typeof(IList<TileLayer>), typeof(MapBase),
|
|
|
|
|
|
new PropertyMetadata(null, (o, e) => ((MapBase)o).TileLayersPropertyChanged((IList<TileLayer>)e.OldValue, (IList<TileLayer>)e.NewValue)));
|
2012-10-12 19:22:49 +02:00
|
|
|
|
|
2012-11-22 21:42:29 +01:00
|
|
|
|
public static readonly DependencyProperty MinZoomLevelProperty = DependencyProperty.Register(
|
2015-11-11 19:48:50 +01:00
|
|
|
|
"MinZoomLevel", typeof(double), typeof(MapBase),
|
|
|
|
|
|
new PropertyMetadata(1d, (o, e) => ((MapBase)o).MinZoomLevelPropertyChanged((double)e.NewValue)));
|
2012-11-22 21:42:29 +01:00
|
|
|
|
|
|
|
|
|
|
public static readonly DependencyProperty MaxZoomLevelProperty = DependencyProperty.Register(
|
2015-11-11 19:48:50 +01:00
|
|
|
|
"MaxZoomLevel", typeof(double), typeof(MapBase),
|
|
|
|
|
|
new PropertyMetadata(19d, (o, e) => ((MapBase)o).MaxZoomLevelPropertyChanged((double)e.NewValue)));
|
|
|
|
|
|
|
|
|
|
|
|
public static readonly DependencyProperty AnimationDurationProperty = DependencyProperty.Register(
|
|
|
|
|
|
"AnimationDuration", typeof(TimeSpan), typeof(MapBase),
|
|
|
|
|
|
new PropertyMetadata(TimeSpan.FromSeconds(0.3)));
|
|
|
|
|
|
|
|
|
|
|
|
public static readonly DependencyProperty AnimationEasingFunctionProperty = DependencyProperty.Register(
|
|
|
|
|
|
"AnimationEasingFunction", typeof(EasingFunctionBase), typeof(MapBase),
|
|
|
|
|
|
new PropertyMetadata(new QuadraticEase { EasingMode = EasingMode.EaseOut }));
|
|
|
|
|
|
|
|
|
|
|
|
public static readonly DependencyProperty TileFadeDurationProperty = DependencyProperty.Register(
|
|
|
|
|
|
"TileFadeDuration", typeof(TimeSpan), typeof(MapBase),
|
|
|
|
|
|
new PropertyMetadata(Tile.FadeDuration, (o, e) => Tile.FadeDuration = (TimeSpan)e.NewValue));
|
2012-10-12 19:22:49 +02:00
|
|
|
|
|
2013-12-05 17:56:04 +01:00
|
|
|
|
internal static readonly DependencyProperty CenterPointProperty = DependencyProperty.Register(
|
2015-11-11 19:48:50 +01:00
|
|
|
|
"CenterPoint", typeof(Point), typeof(MapBase),
|
|
|
|
|
|
new PropertyMetadata(new Point(), (o, e) => ((MapBase)o).CenterPointPropertyChanged((Point)e.NewValue)));
|
2013-12-05 17:56:04 +01:00
|
|
|
|
|
2014-11-19 21:11:14 +01:00
|
|
|
|
private readonly PanelBase tileLayerPanel = new PanelBase();
|
2012-10-12 19:22:49 +02:00
|
|
|
|
private readonly MapTransform mapTransform = new MercatorTransform();
|
2014-11-19 21:11:14 +01:00
|
|
|
|
private readonly MatrixTransform viewportTransform = new MatrixTransform();
|
2015-08-09 20:04:44 +02:00
|
|
|
|
private readonly ScaleTransform scaleTransform = new ScaleTransform();
|
|
|
|
|
|
private readonly RotateTransform rotateTransform = new RotateTransform();
|
|
|
|
|
|
private readonly TransformGroup scaleRotateTransform = new TransformGroup();
|
2014-11-19 21:11:14 +01:00
|
|
|
|
|
2012-10-12 19:22:49 +02:00
|
|
|
|
private Location transformOrigin;
|
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;
|
2012-11-22 21:42:29 +01:00
|
|
|
|
private bool internalPropertyChange;
|
2012-10-12 19:22:49 +02:00
|
|
|
|
|
|
|
|
|
|
public MapBase()
|
|
|
|
|
|
{
|
2015-08-09 20:04:44 +02:00
|
|
|
|
Initialize();
|
2012-10-12 19:22:49 +02:00
|
|
|
|
|
2015-08-09 20:04:44 +02:00
|
|
|
|
scaleRotateTransform.Children.Add(scaleTransform);
|
|
|
|
|
|
scaleRotateTransform.Children.Add(rotateTransform);
|
2014-11-19 21:11:14 +01:00
|
|
|
|
|
2015-08-09 20:04:44 +02:00
|
|
|
|
Children.Add(tileLayerPanel);
|
|
|
|
|
|
TileLayers = new ObservableCollection<TileLayer>();
|
2012-10-12 19:22:49 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2015-08-09 20:04:44 +02:00
|
|
|
|
partial void Initialize(); // Windows Runtime and Silverlight only
|
|
|
|
|
|
partial void RemoveAnimation(DependencyProperty property); // WPF only
|
2012-11-22 21:42:29 +01:00
|
|
|
|
|
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>
|
2014-11-19 21:11:14 +01:00
|
|
|
|
/// Gets or sets the base TileLayer used by the Map control.
|
2012-10-12 19:22:49 +02:00
|
|
|
|
/// </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>
|
2014-11-19 21:11:14 +01:00
|
|
|
|
/// Gets or sets optional multiple TileLayers that are used simultaneously.
|
2015-08-09 20:04:44 +02:00
|
|
|
|
/// The first element in the collection is equal to the value of the TileLayer
|
|
|
|
|
|
/// property. The additional TileLayers usually have transparent backgrounds.
|
2012-10-12 19:22:49 +02:00
|
|
|
|
/// </summary>
|
2014-12-17 21:38:28 +01:00
|
|
|
|
public IList<TileLayer> TileLayers
|
2012-10-12 19:22:49 +02:00
|
|
|
|
{
|
2014-12-17 21:38:28 +01:00
|
|
|
|
get { return (IList<TileLayer>)GetValue(TileLayersProperty); }
|
2014-11-19 21:11:14 +01:00
|
|
|
|
set { SetValue(TileLayersProperty, value); }
|
2012-10-12 19:22:49 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <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.
|
2015-11-11 19:48:50 +01:00
|
|
|
|
/// The default value is 1.
|
2012-11-22 21:42:29 +01:00
|
|
|
|
/// </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.
|
2015-11-11 19:48:50 +01:00
|
|
|
|
/// The default value is 19.
|
2012-11-22 21:42:29 +01:00
|
|
|
|
/// </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>
|
2015-08-09 20:04:44 +02:00
|
|
|
|
/// Gets or sets the map heading, i.e. a clockwise rotation angle in degrees.
|
2012-10-12 19:22:49 +02:00
|
|
|
|
/// </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); }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2015-11-11 19:48:50 +01:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Gets or sets the Duration of the Center, ZoomLevel and Heading animations.
|
|
|
|
|
|
/// The default value is 0.3 seconds.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public TimeSpan AnimationDuration
|
|
|
|
|
|
{
|
|
|
|
|
|
get { return (TimeSpan)GetValue(AnimationDurationProperty); }
|
|
|
|
|
|
set { SetValue(AnimationDurationProperty, value); }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Gets or sets the EasingFunction of the Center, ZoomLevel and Heading animations.
|
|
|
|
|
|
/// The default value is a QuadraticEase with EasingMode.EaseOut.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public EasingFunctionBase AnimationEasingFunction
|
|
|
|
|
|
{
|
|
|
|
|
|
get { return (EasingFunctionBase)GetValue(AnimationEasingFunctionProperty); }
|
|
|
|
|
|
set { SetValue(AnimationEasingFunctionProperty, value); }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Gets or sets the Duration of the Tile Opacity animation.
|
|
|
|
|
|
/// The default value is 0.2 seconds.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public TimeSpan TileFadeDuration
|
|
|
|
|
|
{
|
|
|
|
|
|
get { return (TimeSpan)GetValue(TileFadeDurationProperty); }
|
|
|
|
|
|
set { SetValue(TileFadeDurationProperty, 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>
|
2015-08-09 20:04:44 +02:00
|
|
|
|
/// Gets the transformation from cartesian map coordinates to viewport coordinates (i.e. pixels).
|
2012-10-12 19:22:49 +02:00
|
|
|
|
/// </summary>
|
2015-08-09 20:04:44 +02:00
|
|
|
|
public MatrixTransform ViewportTransform
|
2012-10-12 19:22:49 +02:00
|
|
|
|
{
|
2014-11-19 21:11:14 +01:00
|
|
|
|
get { return viewportTransform; }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2015-08-09 20:04:44 +02:00
|
|
|
|
/// Gets the scaling transformation from meters to viewport coordinate units at the Center location.
|
2012-10-12 19:22:49 +02:00
|
|
|
|
/// </summary>
|
2015-08-09 20:04:44 +02:00
|
|
|
|
public ScaleTransform ScaleTransform
|
2012-10-12 19:22:49 +02:00
|
|
|
|
{
|
|
|
|
|
|
get { return scaleTransform; }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Gets the transformation that rotates by the value of the Heading property.
|
|
|
|
|
|
/// </summary>
|
2015-08-09 20:04:44 +02:00
|
|
|
|
public RotateTransform RotateTransform
|
2012-10-12 19:22:49 +02:00
|
|
|
|
{
|
|
|
|
|
|
get { return rotateTransform; }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Gets the combination of ScaleTransform and RotateTransform
|
|
|
|
|
|
/// </summary>
|
2015-08-09 20:04:44 +02:00
|
|
|
|
public TransformGroup ScaleRotateTransform
|
2012-10-12 19:22:49 +02:00
|
|
|
|
{
|
|
|
|
|
|
get { return scaleRotateTransform; }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2016-01-30 11:50:53 +01:00
|
|
|
|
internal Point MapOrigin { get; private set; }
|
|
|
|
|
|
internal Point ViewportOrigin { get; private set; }
|
|
|
|
|
|
|
2013-05-07 18:12:25 +02:00
|
|
|
|
/// <summary>
|
2014-07-22 20:02:30 +02:00
|
|
|
|
/// Gets the scaling factor from cartesian map coordinates to viewport coordinates.
|
2013-05-07 18:12:25 +02:00
|
|
|
|
/// </summary>
|
2014-07-22 20:02:30 +02:00
|
|
|
|
public double ViewportScale { get; private set; }
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2015-08-09 20:04:44 +02:00
|
|
|
|
/// Gets the scaling factor from meters to viewport coordinate units at the Center location.
|
2014-07-22 20:02:30 +02:00
|
|
|
|
/// </summary>
|
|
|
|
|
|
public double CenterScale { get; private set; }
|
2013-05-07 18:12:25 +02:00
|
|
|
|
|
2014-11-19 21:11:14 +01:00
|
|
|
|
/// <summary>
|
2015-08-09 20:04:44 +02:00
|
|
|
|
/// Gets the map scale at the specified location as viewport coordinate units per meter.
|
2012-11-22 21:42:29 +01:00
|
|
|
|
/// </summary>
|
|
|
|
|
|
public double GetMapScale(Location location)
|
|
|
|
|
|
{
|
2015-08-09 20:04:44 +02:00
|
|
|
|
return mapTransform.RelativeScale(location) *
|
2015-11-11 19:48:50 +01:00
|
|
|
|
Math.Pow(2d, ZoomLevel) * (double)TileSource.TileSize / (TileSource.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)
|
|
|
|
|
|
{
|
2014-11-19 21:11:14 +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)
|
|
|
|
|
|
{
|
2014-11-19 21:11:14 +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.
|
2015-08-09 20:04:44 +02:00
|
|
|
|
/// This origin location is automatically reset when the Center property is set by application code.
|
2012-10-12 19:22:49 +02:00
|
|
|
|
/// </summary>
|
|
|
|
|
|
public void SetTransformOrigin(Location origin)
|
|
|
|
|
|
{
|
|
|
|
|
|
transformOrigin = origin;
|
2015-08-09 20:04:44 +02:00
|
|
|
|
ViewportOrigin = LocationToViewportPoint(origin);
|
2012-10-12 19:22:49 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Sets a temporary origin point in viewport coordinates for scaling and rotation transformations.
|
2015-08-09 20:04:44 +02:00
|
|
|
|
/// This origin point is automatically reset when the Center property is set by application code.
|
2012-10-12 19:22:49 +02:00
|
|
|
|
/// </summary>
|
|
|
|
|
|
public void SetTransformOrigin(Point origin)
|
|
|
|
|
|
{
|
2016-01-14 17:56:25 +01:00
|
|
|
|
transformOrigin = ViewportPointToLocation(origin);
|
|
|
|
|
|
ViewportOrigin = origin;
|
2012-10-12 19:22:49 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2015-08-09 20:04:44 +02:00
|
|
|
|
/// Resets the temporary transform origin point set by SetTransformOrigin.
|
2012-10-12 19:22:49 +02:00
|
|
|
|
/// </summary>
|
|
|
|
|
|
public void ResetTransformOrigin()
|
|
|
|
|
|
{
|
|
|
|
|
|
transformOrigin = null;
|
2015-08-09 20:04:44 +02:00
|
|
|
|
ViewportOrigin = new Point(RenderSize.Width / 2d, RenderSize.Height / 2d);
|
2012-10-12 19:22:49 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2016-01-30 11:50:53 +01:00
|
|
|
|
/// Changes the Center property according to the specified map translation in viewport coordinates.
|
2012-10-12 19:22:49 +02:00
|
|
|
|
/// </summary>
|
2012-11-22 21:42:29 +01:00
|
|
|
|
public void TranslateMap(Point translation)
|
2012-10-12 19:22:49 +02:00
|
|
|
|
{
|
2013-11-22 18:46:45 +01:00
|
|
|
|
if (transformOrigin != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
ResetTransformOrigin();
|
2016-01-30 11:50:53 +01:00
|
|
|
|
UpdateTransform();
|
2013-11-22 18:46:45 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2012-10-12 19:22:49 +02:00
|
|
|
|
if (translation.X != 0d || translation.Y != 0d)
|
|
|
|
|
|
{
|
2016-01-30 11:50:53 +01:00
|
|
|
|
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);
|
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
|
|
|
|
{
|
2016-01-30 11:50:53 +01:00
|
|
|
|
if (rotation != 0d || scale != 1d)
|
|
|
|
|
|
{
|
|
|
|
|
|
transformOrigin = ViewportPointToLocation(origin);
|
|
|
|
|
|
ViewportOrigin = new Point(origin.X + translation.X, origin.Y + translation.Y);
|
2013-04-04 18:01:13 +02:00
|
|
|
|
|
2016-01-30 11:50:53 +01:00
|
|
|
|
if (rotation != 0d)
|
|
|
|
|
|
{
|
|
|
|
|
|
var heading = (((Heading + rotation) % 360d) + 360d) % 360d;
|
|
|
|
|
|
InternalSetValue(HeadingProperty, heading);
|
|
|
|
|
|
InternalSetValue(TargetHeadingProperty, heading);
|
|
|
|
|
|
}
|
2013-04-04 18:01:13 +02:00
|
|
|
|
|
2016-01-30 11:50:53 +01:00
|
|
|
|
if (scale != 1d)
|
|
|
|
|
|
{
|
|
|
|
|
|
var zoomLevel = Math.Min(Math.Max(ZoomLevel + Math.Log(scale, 2d), MinZoomLevel), MaxZoomLevel);
|
|
|
|
|
|
InternalSetValue(ZoomLevelProperty, zoomLevel);
|
|
|
|
|
|
InternalSetValue(TargetZoomLevelProperty, zoomLevel);
|
|
|
|
|
|
}
|
2013-04-04 18:01:13 +02:00
|
|
|
|
|
2016-01-30 11:50:53 +01:00
|
|
|
|
UpdateTransform(true);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
2013-04-04 18:01:13 +02:00
|
|
|
|
{
|
2016-01-30 11:50:53 +01:00
|
|
|
|
TranslateMap(translation); // more precise
|
2012-10-12 19:22:49 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <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)
|
|
|
|
|
|
{
|
2016-02-21 21:39:37 +01:00
|
|
|
|
zoomLevel = Math.Min(Math.Max(zoomLevel, MinZoomLevel), MaxZoomLevel);
|
|
|
|
|
|
|
|
|
|
|
|
if (TargetZoomLevel != zoomLevel)
|
|
|
|
|
|
{
|
|
|
|
|
|
SetTransformOrigin(origin);
|
|
|
|
|
|
TargetZoomLevel = zoomLevel;
|
|
|
|
|
|
}
|
2012-10-12 19:22:49 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2013-11-17 16:52:03 +01:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Sets the TargetZoomLevel and TargetCenter properties such that the specified bounding box
|
|
|
|
|
|
/// fits into the current viewport. The TargetHeading property is set to zero.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public void ZoomToBounds(Location southWest, Location northEast)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (southWest.Latitude < northEast.Latitude && southWest.Longitude < northEast.Longitude)
|
|
|
|
|
|
{
|
2014-11-19 21:11:14 +01:00
|
|
|
|
var p1 = mapTransform.Transform(southWest);
|
|
|
|
|
|
var p2 = mapTransform.Transform(northEast);
|
2015-11-11 19:48:50 +01:00
|
|
|
|
var lonScale = RenderSize.Width / (p2.X - p1.X) * 360d / (double)TileSource.TileSize;
|
|
|
|
|
|
var latScale = RenderSize.Height / (p2.Y - p1.Y) * 360d / (double)TileSource.TileSize;
|
2013-11-17 16:52:03 +01:00
|
|
|
|
var lonZoom = Math.Log(lonScale, 2d);
|
|
|
|
|
|
var latZoom = Math.Log(latScale, 2d);
|
|
|
|
|
|
|
|
|
|
|
|
TargetZoomLevel = Math.Min(lonZoom, latZoom);
|
2014-11-19 21:11:14 +01:00
|
|
|
|
TargetCenter = mapTransform.Transform(new Point((p1.X + p2.X) / 2d, (p1.Y + p2.Y) / 2d));
|
2013-11-17 16:52:03 +01:00
|
|
|
|
TargetHeading = 0d;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2014-11-19 21:11:14 +01:00
|
|
|
|
private void TileLayerPropertyChanged(TileLayer tileLayer)
|
2012-10-12 19:22:49 +02:00
|
|
|
|
{
|
2014-11-19 21:11:14 +01:00
|
|
|
|
if (tileLayer != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (TileLayers == null)
|
|
|
|
|
|
{
|
2014-12-17 21:38:28 +01:00
|
|
|
|
TileLayers = new ObservableCollection<TileLayer>(new TileLayer[] { tileLayer });
|
2014-11-19 21:11:14 +01:00
|
|
|
|
}
|
2014-11-20 17:13:11 +01:00
|
|
|
|
else if (TileLayers.Count == 0)
|
2014-11-19 21:11:14 +01:00
|
|
|
|
{
|
|
|
|
|
|
TileLayers.Add(tileLayer);
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (TileLayers[0] != tileLayer)
|
|
|
|
|
|
{
|
|
|
|
|
|
TileLayers[0] = tileLayer;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2014-12-17 21:38:28 +01:00
|
|
|
|
private void TileLayersPropertyChanged(IList<TileLayer> oldTileLayers, IList<TileLayer> newTileLayers)
|
2014-11-19 21:11:14 +01:00
|
|
|
|
{
|
|
|
|
|
|
if (oldTileLayers != null)
|
2012-10-12 19:22:49 +02:00
|
|
|
|
{
|
2014-12-17 21:38:28 +01:00
|
|
|
|
var oldCollection = oldTileLayers as INotifyCollectionChanged;
|
|
|
|
|
|
if (oldCollection != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
oldCollection.CollectionChanged -= TileLayerCollectionChanged;
|
|
|
|
|
|
}
|
2014-11-20 17:13:11 +01:00
|
|
|
|
|
|
|
|
|
|
TileLayer = null;
|
2015-06-09 20:09:57 +02:00
|
|
|
|
ClearTileLayers();
|
2014-11-19 21:11:14 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (newTileLayers != null)
|
|
|
|
|
|
{
|
2014-11-20 17:13:11 +01:00
|
|
|
|
TileLayer = newTileLayers.FirstOrDefault();
|
2014-11-19 21:11:14 +01:00
|
|
|
|
AddTileLayers(0, newTileLayers);
|
2014-11-20 17:13:11 +01:00
|
|
|
|
|
2014-12-17 21:38:28 +01:00
|
|
|
|
var newCollection = newTileLayers as INotifyCollectionChanged;
|
|
|
|
|
|
if (newCollection != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
newCollection.CollectionChanged += TileLayerCollectionChanged;
|
|
|
|
|
|
}
|
2012-10-12 19:22:49 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void TileLayerCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
|
|
|
|
|
|
{
|
|
|
|
|
|
switch (e.Action)
|
|
|
|
|
|
{
|
|
|
|
|
|
case NotifyCollectionChangedAction.Add:
|
2014-11-19 21:11:14 +01:00
|
|
|
|
AddTileLayers(e.NewStartingIndex, e.NewItems.Cast<TileLayer>());
|
2012-10-12 19:22:49 +02:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case NotifyCollectionChangedAction.Remove:
|
2014-11-19 21:11:14 +01:00
|
|
|
|
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:
|
2014-11-19 21:11:14 +01:00
|
|
|
|
RemoveTileLayers(e.NewStartingIndex, e.OldItems.Count);
|
|
|
|
|
|
AddTileLayers(e.NewStartingIndex, e.NewItems.Cast<TileLayer>());
|
2012-10-12 19:22:49 +02:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case NotifyCollectionChangedAction.Reset:
|
2015-06-09 20:09:57 +02:00
|
|
|
|
ClearTileLayers();
|
2012-10-12 19:22:49 +02:00
|
|
|
|
if (e.NewItems != null)
|
|
|
|
|
|
{
|
2014-11-19 21:11:14 +01:00
|
|
|
|
AddTileLayers(0, e.NewItems.Cast<TileLayer>());
|
2012-10-12 19:22:49 +02:00
|
|
|
|
}
|
|
|
|
|
|
break;
|
2012-11-22 21:42:29 +01:00
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
|
break;
|
2012-10-12 19:22:49 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2014-11-20 17:13:11 +01:00
|
|
|
|
var tileLayer = TileLayers.FirstOrDefault();
|
|
|
|
|
|
|
|
|
|
|
|
if (TileLayer != tileLayer)
|
|
|
|
|
|
{
|
|
|
|
|
|
TileLayer = tileLayer;
|
|
|
|
|
|
}
|
2012-10-12 19:22:49 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2014-11-19 21:11:14 +01:00
|
|
|
|
private void AddTileLayers(int index, IEnumerable<TileLayer> tileLayers)
|
2012-10-12 19:22:49 +02:00
|
|
|
|
{
|
2014-11-19 21:11:14 +01:00
|
|
|
|
foreach (var tileLayer in tileLayers)
|
2012-10-12 19:22:49 +02:00
|
|
|
|
{
|
2014-11-19 21:11:14 +01:00
|
|
|
|
if (index == 0)
|
2014-07-22 20:02:30 +02:00
|
|
|
|
{
|
2014-11-19 21:11:14 +01:00
|
|
|
|
if (tileLayer.Background != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
Background = tileLayer.Background;
|
|
|
|
|
|
}
|
2012-10-12 19:22:49 +02:00
|
|
|
|
|
2014-11-19 21:11:14 +01:00
|
|
|
|
if (tileLayer.Foreground != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
Foreground = tileLayer.Foreground;
|
|
|
|
|
|
}
|
2012-11-22 21:42:29 +01:00
|
|
|
|
}
|
2012-10-12 19:22:49 +02:00
|
|
|
|
|
2014-11-19 21:11:14 +01:00
|
|
|
|
tileLayerPanel.Children.Insert(index++, tileLayer);
|
2012-10-12 19:22:49 +02:00
|
|
|
|
}
|
2014-11-19 21:11:14 +01:00
|
|
|
|
}
|
2012-10-12 19:22:49 +02:00
|
|
|
|
|
2014-11-19 21:11:14 +01:00
|
|
|
|
private void RemoveTileLayers(int index, int count)
|
|
|
|
|
|
{
|
|
|
|
|
|
while (count-- > 0)
|
2012-10-12 19:22:49 +02:00
|
|
|
|
{
|
2014-11-19 21:11:14 +01:00
|
|
|
|
tileLayerPanel.Children.RemoveAt(index + count);
|
2013-04-21 23:56:08 +02:00
|
|
|
|
}
|
2012-10-12 19:22:49 +02:00
|
|
|
|
|
2014-11-19 21:11:14 +01:00
|
|
|
|
if (index == 0)
|
2013-04-21 23:56:08 +02:00
|
|
|
|
{
|
2014-11-19 21:11:14 +01:00
|
|
|
|
ClearValue(BackgroundProperty);
|
|
|
|
|
|
ClearValue(ForegroundProperty);
|
2012-10-12 19:22:49 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2015-06-09 20:09:57 +02:00
|
|
|
|
private void ClearTileLayers()
|
|
|
|
|
|
{
|
|
|
|
|
|
tileLayerPanel.Children.Clear();
|
|
|
|
|
|
ClearValue(BackgroundProperty);
|
|
|
|
|
|
ClearValue(ForegroundProperty);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
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-11-21 21:16:29 +01:00
|
|
|
|
private void AdjustCenterProperty(DependencyProperty property, ref Location center)
|
2012-10-12 19:22:49 +02:00
|
|
|
|
{
|
2013-11-21 21:16:29 +01:00
|
|
|
|
if (center == null)
|
2013-11-17 16:52:03 +01:00
|
|
|
|
{
|
2013-11-21 21:16:29 +01:00
|
|
|
|
center = new Location();
|
|
|
|
|
|
InternalSetValue(property, center);
|
2013-11-17 16:52:03 +01:00
|
|
|
|
}
|
2013-11-22 18:46:45 +01:00
|
|
|
|
else if (center.Longitude < -180d || center.Longitude > 180d ||
|
|
|
|
|
|
center.Latitude < -mapTransform.MaxLatitude || center.Latitude > mapTransform.MaxLatitude)
|
2013-04-04 18:01:13 +02:00
|
|
|
|
{
|
2013-11-22 18:46:45 +01:00
|
|
|
|
center = new Location(
|
|
|
|
|
|
Math.Min(Math.Max(center.Latitude, -mapTransform.MaxLatitude), mapTransform.MaxLatitude),
|
|
|
|
|
|
Location.NormalizeLongitude(center.Longitude));
|
|
|
|
|
|
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-11-21 21:16:29 +01:00
|
|
|
|
AdjustCenterProperty(CenterProperty, ref center);
|
2012-10-12 19:22:49 +02:00
|
|
|
|
UpdateTransform();
|
|
|
|
|
|
|
2012-11-22 21:42:29 +01:00
|
|
|
|
if (centerAnimation == null)
|
|
|
|
|
|
{
|
2013-04-04 18:01:13 +02:00
|
|
|
|
InternalSetValue(TargetCenterProperty, center);
|
2014-11-19 21:11:14 +01:00
|
|
|
|
InternalSetValue(CenterPointProperty, mapTransform.Transform(center));
|
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-11-21 21:16:29 +01:00
|
|
|
|
AdjustCenterProperty(TargetCenterProperty, ref targetCenter);
|
2012-11-22 21:42:29 +01:00
|
|
|
|
|
2014-07-09 21:27:28 +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
|
|
|
|
|
|
{
|
2016-07-03 00:03:38 +02:00
|
|
|
|
From = mapTransform.Transform(Center),
|
2016-04-19 19:36:03 +02:00
|
|
|
|
To = mapTransform.Transform(new Location(
|
|
|
|
|
|
targetCenter.Latitude,
|
|
|
|
|
|
Location.NearestLongitude(targetCenter.Longitude, Center.Longitude))),
|
2014-11-19 21:11:14 +01:00
|
|
|
|
Duration = AnimationDuration,
|
2015-01-26 19:45:05 +01:00
|
|
|
|
EasingFunction = AnimationEasingFunction
|
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);
|
2014-11-19 21:11:14 +01:00
|
|
|
|
InternalSetValue(CenterPointProperty, mapTransform.Transform(TargetCenter));
|
2013-10-30 18:23:13 +01:00
|
|
|
|
RemoveAnimation(CenterPointProperty); // remove holding animation in WPF
|
2012-11-22 21:42:29 +01:00
|
|
|
|
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
|
|
|
|
{
|
2016-07-03 00:03:38 +02:00
|
|
|
|
System.Diagnostics.Debug.WriteLine("CenterPoint: {0}", centerPoint);
|
2012-11-22 21:42:29 +01:00
|
|
|
|
if (!internalPropertyChange)
|
|
|
|
|
|
{
|
2013-11-21 21:16:29 +01:00
|
|
|
|
centerPoint.X = Location.NormalizeLongitude(centerPoint.X);
|
2014-11-19 21:11:14 +01:00
|
|
|
|
InternalSetValue(CenterProperty, mapTransform.Transform(centerPoint));
|
2012-11-22 21:42:29 +01:00
|
|
|
|
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
|
|
|
|
{
|
2013-11-21 21:16:29 +01:00
|
|
|
|
if (minZoomLevel < 0d || minZoomLevel > MaxZoomLevel)
|
2012-11-22 21:42:29 +01:00
|
|
|
|
{
|
2013-11-21 21:16:29 +01:00
|
|
|
|
minZoomLevel = Math.Min(Math.Max(minZoomLevel, 0d), MaxZoomLevel);
|
2013-11-17 16:52:03 +01:00
|
|
|
|
InternalSetValue(MinZoomLevelProperty, minZoomLevel);
|
2012-11-22 21:42:29 +01:00
|
|
|
|
}
|
2013-11-17 16:52:03 +01:00
|
|
|
|
|
|
|
|
|
|
if (ZoomLevel < minZoomLevel)
|
2012-11-22 21:42:29 +01:00
|
|
|
|
{
|
|
|
|
|
|
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
|
|
|
|
{
|
2013-11-21 21:16:29 +01:00
|
|
|
|
if (maxZoomLevel < MinZoomLevel || maxZoomLevel > MaximumZoomLevel)
|
2012-10-12 19:22:49 +02:00
|
|
|
|
{
|
2013-11-21 21:16:29 +01:00
|
|
|
|
maxZoomLevel = Math.Min(Math.Max(maxZoomLevel, MinZoomLevel), MaximumZoomLevel);
|
2013-11-17 16:52:03 +01:00
|
|
|
|
InternalSetValue(MaxZoomLevelProperty, maxZoomLevel);
|
2012-11-22 21:42:29 +01:00
|
|
|
|
}
|
2013-11-17 16:52:03 +01:00
|
|
|
|
|
|
|
|
|
|
if (ZoomLevel > maxZoomLevel)
|
2012-11-22 21:42:29 +01:00
|
|
|
|
{
|
|
|
|
|
|
ZoomLevel = maxZoomLevel;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2013-11-21 21:16:29 +01:00
|
|
|
|
private void AdjustZoomLevelProperty(DependencyProperty property, ref double zoomLevel)
|
2012-11-22 21:42:29 +01:00
|
|
|
|
{
|
2013-11-21 21:16:29 +01:00
|
|
|
|
if (zoomLevel < MinZoomLevel || zoomLevel > MaxZoomLevel)
|
2012-11-22 21:42:29 +01:00
|
|
|
|
{
|
2013-11-21 21:16:29 +01:00
|
|
|
|
zoomLevel = Math.Min(Math.Max(zoomLevel, MinZoomLevel), MaxZoomLevel);
|
2013-11-17 16:52:03 +01:00
|
|
|
|
InternalSetValue(property, zoomLevel);
|
2012-10-12 19:22:49 +02:00
|
|
|
|
}
|
2012-11-22 21:42:29 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void ZoomLevelPropertyChanged(double zoomLevel)
|
|
|
|
|
|
{
|
2013-11-17 16:52:03 +01:00
|
|
|
|
if (!internalPropertyChange)
|
2012-10-12 19:22:49 +02:00
|
|
|
|
{
|
2013-11-21 21:16:29 +01:00
|
|
|
|
AdjustZoomLevelProperty(ZoomLevelProperty, ref zoomLevel);
|
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)
|
|
|
|
|
|
{
|
2013-11-17 16:52:03 +01:00
|
|
|
|
if (!internalPropertyChange)
|
2012-10-12 19:22:49 +02:00
|
|
|
|
{
|
2013-11-21 21:16:29 +01:00
|
|
|
|
AdjustZoomLevelProperty(TargetZoomLevelProperty, ref targetZoomLevel);
|
2012-10-12 19:22:49 +02:00
|
|
|
|
|
2013-11-17 16:52:03 +01:00
|
|
|
|
if (targetZoomLevel != ZoomLevel)
|
2012-10-12 19:22:49 +02:00
|
|
|
|
{
|
2013-11-17 16:52:03 +01:00
|
|
|
|
if (zoomLevelAnimation != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
zoomLevelAnimation.Completed -= ZoomLevelAnimationCompleted;
|
|
|
|
|
|
}
|
2012-10-12 19:22:49 +02:00
|
|
|
|
|
2013-11-17 16:52:03 +01:00
|
|
|
|
zoomLevelAnimation = new DoubleAnimation
|
|
|
|
|
|
{
|
|
|
|
|
|
To = targetZoomLevel,
|
2014-11-19 21:11:14 +01:00
|
|
|
|
Duration = AnimationDuration,
|
2015-01-26 19:45:05 +01:00
|
|
|
|
EasingFunction = AnimationEasingFunction
|
2013-11-17 16:52:03 +01:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
zoomLevelAnimation.Completed += ZoomLevelAnimationCompleted;
|
|
|
|
|
|
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);
|
2013-10-30 18:23:13 +01:00
|
|
|
|
RemoveAnimation(ZoomLevelProperty); // remove holding animation in WPF
|
2016-02-21 00:06:54 +01:00
|
|
|
|
UpdateTransform(true);
|
2012-11-22 21:42:29 +01:00
|
|
|
|
}
|
2012-10-12 19:22:49 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2013-11-21 21:16:29 +01:00
|
|
|
|
private void AdjustHeadingProperty(DependencyProperty property, ref double heading)
|
2012-10-12 19:22:49 +02:00
|
|
|
|
{
|
2013-12-20 17:05:10 +01:00
|
|
|
|
if (heading < 0d || heading > 360d)
|
2012-10-12 19:22:49 +02:00
|
|
|
|
{
|
2013-11-21 21:16:29 +01:00
|
|
|
|
heading = ((heading % 360d) + 360d) % 360d;
|
2013-11-17 16:52:03 +01:00
|
|
|
|
InternalSetValue(property, heading);
|
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
|
|
|
|
{
|
2013-11-21 21:16:29 +01:00
|
|
|
|
AdjustHeadingProperty(HeadingProperty, ref heading);
|
2012-11-22 21:42:29 +01:00
|
|
|
|
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
|
|
|
|
{
|
2013-11-21 21:16:29 +01:00
|
|
|
|
AdjustHeadingProperty(TargetHeadingProperty, ref targetHeading);
|
2012-11-22 21:42:29 +01:00
|
|
|
|
|
|
|
|
|
|
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,
|
2014-11-19 21:11:14 +01:00
|
|
|
|
Duration = AnimationDuration,
|
2015-01-26 19:45:05 +01:00
|
|
|
|
EasingFunction = AnimationEasingFunction
|
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);
|
2013-10-30 18:23:13 +01:00
|
|
|
|
RemoveAnimation(HeadingProperty); // remove holding animation in WPF
|
2012-11-22 21:42:29 +01:00
|
|
|
|
UpdateTransform();
|
|
|
|
|
|
}
|
2012-10-12 19:22:49 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2016-01-30 11:50:53 +01:00
|
|
|
|
private void UpdateTransform(bool resetOrigin = false)
|
2012-10-12 19:22:49 +02:00
|
|
|
|
{
|
2015-11-11 19:48:50 +01:00
|
|
|
|
var center = transformOrigin ?? Center;
|
2012-10-12 19:22:49 +02:00
|
|
|
|
|
2015-11-11 19:48:50 +01:00
|
|
|
|
SetViewportTransform(center);
|
2014-10-29 18:22:50 +01:00
|
|
|
|
|
2015-11-11 19:48:50 +01:00
|
|
|
|
if (transformOrigin != null)
|
|
|
|
|
|
{
|
2013-04-04 18:01:13 +02:00
|
|
|
|
center = ViewportPointToLocation(new Point(RenderSize.Width / 2d, RenderSize.Height / 2d));
|
2013-11-22 18:46:45 +01:00
|
|
|
|
center.Longitude = Location.NormalizeLongitude(center.Longitude);
|
2013-05-13 23:49:48 +02:00
|
|
|
|
|
2013-11-22 18:46:45 +01:00
|
|
|
|
if (center.Latitude < -mapTransform.MaxLatitude || center.Latitude > mapTransform.MaxLatitude)
|
|
|
|
|
|
{
|
|
|
|
|
|
center.Latitude = Math.Min(Math.Max(center.Latitude, -mapTransform.MaxLatitude), mapTransform.MaxLatitude);
|
2016-01-30 11:50:53 +01:00
|
|
|
|
resetOrigin = true;
|
2013-11-22 18:46:45 +01:00
|
|
|
|
}
|
2013-04-04 18:01:13 +02:00
|
|
|
|
|
|
|
|
|
|
InternalSetValue(CenterProperty, center);
|
|
|
|
|
|
|
2013-11-21 21:16:29 +01:00
|
|
|
|
if (centerAnimation == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
InternalSetValue(TargetCenterProperty, center);
|
2014-11-19 21:11:14 +01:00
|
|
|
|
InternalSetValue(CenterPointProperty, mapTransform.Transform(center));
|
2013-11-21 21:16:29 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2016-01-30 11:50:53 +01:00
|
|
|
|
if (resetOrigin)
|
2013-04-04 18:01:13 +02:00
|
|
|
|
{
|
|
|
|
|
|
ResetTransformOrigin();
|
2013-12-20 17:05:10 +01:00
|
|
|
|
SetViewportTransform(center);
|
2013-04-04 18:01:13 +02:00
|
|
|
|
}
|
2012-10-12 19:22:49 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2015-11-11 19:48:50 +01:00
|
|
|
|
CenterScale = ViewportScale * mapTransform.RelativeScale(center) / TileSource.MetersPerDegree;
|
2015-08-09 20:04:44 +02:00
|
|
|
|
scaleTransform.ScaleX = CenterScale;
|
|
|
|
|
|
scaleTransform.ScaleY = CenterScale;
|
|
|
|
|
|
rotateTransform.Angle = Heading;
|
2012-10-12 19:22:49 +02:00
|
|
|
|
|
|
|
|
|
|
OnViewportChanged();
|
|
|
|
|
|
}
|
2013-05-13 23:49:48 +02:00
|
|
|
|
|
2015-08-09 20:04:44 +02:00
|
|
|
|
protected override void OnViewportChanged()
|
2014-11-19 21:11:14 +01:00
|
|
|
|
{
|
2015-08-09 20:04:44 +02:00
|
|
|
|
base.OnViewportChanged();
|
2014-11-19 21:11:14 +01:00
|
|
|
|
|
2016-07-03 00:03:38 +02:00
|
|
|
|
ViewportChanged?.Invoke(this, EventArgs.Empty);
|
2013-05-13 23:49:48 +02:00
|
|
|
|
}
|
2012-10-12 19:22:49 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|