2017-06-25 23:05:48 +02:00
|
|
|
|
// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
|
2024-02-03 21:01:53 +01:00
|
|
|
|
// Copyright © 2024 Clemens Fischer
|
2012-11-22 21:42:29 +01:00
|
|
|
|
// Licensed under the Microsoft Public License (Ms-PL)
|
|
|
|
|
|
|
|
|
|
|
|
using System.Windows;
|
2024-05-24 15:14:05 +02:00
|
|
|
|
using System.Windows.Documents;
|
|
|
|
|
|
using System.Windows.Media;
|
2024-05-21 13:51:10 +02:00
|
|
|
|
using System.Windows.Media.Animation;
|
2012-11-22 21:42:29 +01:00
|
|
|
|
|
|
|
|
|
|
namespace MapControl
|
|
|
|
|
|
{
|
|
|
|
|
|
public partial class MapBase
|
|
|
|
|
|
{
|
2024-05-24 15:14:05 +02:00
|
|
|
|
public static readonly DependencyProperty ForegroundProperty =
|
|
|
|
|
|
DependencyPropertyHelper.AddOwner<MapBase, Brush>(TextElement.ForegroundProperty);
|
|
|
|
|
|
|
2024-05-21 13:51:10 +02:00
|
|
|
|
public static readonly DependencyProperty AnimationEasingFunctionProperty =
|
|
|
|
|
|
DependencyPropertyHelper.Register<MapBase, IEasingFunction>(nameof(AnimationEasingFunction),
|
|
|
|
|
|
new QuadraticEase { EasingMode = EasingMode.EaseOut });
|
|
|
|
|
|
|
|
|
|
|
|
public static readonly DependencyProperty CenterProperty =
|
2024-05-23 18:22:52 +02:00
|
|
|
|
DependencyPropertyHelper.Register<MapBase, Location>(nameof(Center), new Location(),
|
2024-05-21 13:51:10 +02:00
|
|
|
|
(map, oldValue, newValue) => map.CenterPropertyChanged(newValue),
|
2024-05-23 18:22:52 +02:00
|
|
|
|
(map, value) => map.CoerceCenterProperty(value),
|
|
|
|
|
|
true);
|
2024-05-21 13:51:10 +02:00
|
|
|
|
|
|
|
|
|
|
public static readonly DependencyProperty TargetCenterProperty =
|
2024-05-23 18:22:52 +02:00
|
|
|
|
DependencyPropertyHelper.Register<MapBase, Location>(nameof(TargetCenter), new Location(),
|
2024-05-21 13:51:10 +02:00
|
|
|
|
(map, oldValue, newValue) => map.TargetCenterPropertyChanged(newValue),
|
2024-05-23 18:22:52 +02:00
|
|
|
|
(map, value) => map.CoerceCenterProperty(value),
|
|
|
|
|
|
true);
|
2024-05-21 13:51:10 +02:00
|
|
|
|
|
|
|
|
|
|
public static readonly DependencyProperty MinZoomLevelProperty =
|
2024-05-24 22:00:16 +02:00
|
|
|
|
DependencyPropertyHelper.Register<MapBase, double>(nameof(MinZoomLevel), 1d,
|
2024-05-21 13:51:10 +02:00
|
|
|
|
(map, oldValue, newValue) => map.MinZoomLevelPropertyChanged(newValue),
|
|
|
|
|
|
(map, value) => map.CoerceMinZoomLevelProperty(value));
|
|
|
|
|
|
|
|
|
|
|
|
public static readonly DependencyProperty MaxZoomLevelProperty =
|
2024-05-23 18:22:52 +02:00
|
|
|
|
DependencyPropertyHelper.Register<MapBase, double>(nameof(MaxZoomLevel), 20d,
|
2024-05-21 13:51:10 +02:00
|
|
|
|
(map, oldValue, newValue) => map.MaxZoomLevelPropertyChanged(newValue),
|
2024-05-22 09:42:21 +02:00
|
|
|
|
(map, value) => map.CoerceMaxZoomLevelProperty(value));
|
2024-05-21 13:51:10 +02:00
|
|
|
|
|
|
|
|
|
|
public static readonly DependencyProperty ZoomLevelProperty =
|
2024-05-24 22:00:16 +02:00
|
|
|
|
DependencyPropertyHelper.Register<MapBase, double>(nameof(ZoomLevel), 1d,
|
2024-05-21 13:51:10 +02:00
|
|
|
|
(map, oldValue, newValue) => map.ZoomLevelPropertyChanged(newValue),
|
2024-05-23 18:22:52 +02:00
|
|
|
|
(map, value) => map.CoerceZoomLevelProperty(value),
|
|
|
|
|
|
true);
|
2024-05-21 13:51:10 +02:00
|
|
|
|
|
|
|
|
|
|
public static readonly DependencyProperty TargetZoomLevelProperty =
|
2024-05-24 22:00:16 +02:00
|
|
|
|
DependencyPropertyHelper.Register<MapBase, double>(nameof(TargetZoomLevel), 1d,
|
2024-05-21 13:51:10 +02:00
|
|
|
|
(map, oldValue, newValue) => map.TargetZoomLevelPropertyChanged(newValue),
|
2024-05-23 18:22:52 +02:00
|
|
|
|
(map, value) => map.CoerceZoomLevelProperty(value),
|
|
|
|
|
|
true);
|
2024-05-21 13:51:10 +02:00
|
|
|
|
|
|
|
|
|
|
public static readonly DependencyProperty HeadingProperty =
|
2024-05-23 18:22:52 +02:00
|
|
|
|
DependencyPropertyHelper.Register<MapBase, double>(nameof(Heading), 0d,
|
2024-05-21 13:51:10 +02:00
|
|
|
|
(map, oldValue, newValue) => map.HeadingPropertyChanged(newValue),
|
2024-05-23 18:22:52 +02:00
|
|
|
|
(map, value) => map.CoerceHeadingProperty(value),
|
|
|
|
|
|
true);
|
2024-05-21 13:51:10 +02:00
|
|
|
|
|
|
|
|
|
|
public static readonly DependencyProperty TargetHeadingProperty =
|
2024-05-23 18:22:52 +02:00
|
|
|
|
DependencyPropertyHelper.Register<MapBase, double>(nameof(TargetHeading), 0d,
|
2024-05-21 13:51:10 +02:00
|
|
|
|
(map, oldValue, newValue) => map.TargetHeadingPropertyChanged(newValue),
|
2024-05-23 18:22:52 +02:00
|
|
|
|
(map, value) => map.CoerceHeadingProperty(value),
|
|
|
|
|
|
true);
|
2024-05-21 13:51:10 +02:00
|
|
|
|
|
|
|
|
|
|
private static readonly DependencyPropertyKey ViewScalePropertyKey =
|
2024-05-24 21:27:04 +02:00
|
|
|
|
DependencyPropertyHelper.RegisterReadOnly<MapBase, double>(nameof(ViewScale));
|
2022-11-02 19:49:18 +01:00
|
|
|
|
|
|
|
|
|
|
public static readonly DependencyProperty ViewScaleProperty = ViewScalePropertyKey.DependencyProperty;
|
|
|
|
|
|
|
2024-05-21 13:51:10 +02:00
|
|
|
|
private LocationAnimation centerAnimation;
|
|
|
|
|
|
private DoubleAnimation zoomLevelAnimation;
|
|
|
|
|
|
private DoubleAnimation headingAnimation;
|
2018-03-11 21:03:44 +01:00
|
|
|
|
|
2012-11-22 21:42:29 +01:00
|
|
|
|
static MapBase()
|
|
|
|
|
|
{
|
2017-06-25 23:05:48 +02:00
|
|
|
|
ClipToBoundsProperty.OverrideMetadata(typeof(MapBase), new FrameworkPropertyMetadata(true));
|
2021-01-17 21:39:42 +01:00
|
|
|
|
DefaultStyleKeyProperty.OverrideMetadata(typeof(MapBase), new FrameworkPropertyMetadata(typeof(MapBase)));
|
2012-11-22 21:42:29 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2024-05-21 13:51:10 +02:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Gets or sets the EasingFunction of the Center, ZoomLevel and Heading animations.
|
|
|
|
|
|
/// The default value is a QuadraticEase with EasingMode.EaseOut.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public IEasingFunction AnimationEasingFunction
|
|
|
|
|
|
{
|
|
|
|
|
|
get => (IEasingFunction)GetValue(AnimationEasingFunctionProperty);
|
|
|
|
|
|
set => SetValue(AnimationEasingFunctionProperty, value);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Gets the scaling factor from projected map coordinates to view coordinates,
|
|
|
|
|
|
/// as pixels per meter.
|
|
|
|
|
|
/// </summary>
|
2024-05-22 09:42:21 +02:00
|
|
|
|
public double ViewScale
|
2024-05-21 13:51:10 +02:00
|
|
|
|
{
|
2024-05-22 09:42:21 +02:00
|
|
|
|
get => (double)GetValue(ViewScaleProperty);
|
|
|
|
|
|
private set => SetValue(ViewScalePropertyKey, value);
|
2024-05-21 13:51:10 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2013-04-04 18:01:13 +02:00
|
|
|
|
protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
|
2012-11-22 21:42:29 +01:00
|
|
|
|
{
|
2013-04-04 18:01:13 +02:00
|
|
|
|
base.OnRenderSizeChanged(sizeInfo);
|
2015-08-09 20:04:44 +02:00
|
|
|
|
|
2017-06-25 23:05:48 +02:00
|
|
|
|
ResetTransformCenter();
|
2012-11-22 21:42:29 +01:00
|
|
|
|
UpdateTransform();
|
|
|
|
|
|
}
|
2022-11-02 19:49:18 +01:00
|
|
|
|
|
2024-05-21 13:51:10 +02:00
|
|
|
|
private void CenterPropertyChanged(Location center)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!internalPropertyChange)
|
|
|
|
|
|
{
|
|
|
|
|
|
UpdateTransform();
|
|
|
|
|
|
|
|
|
|
|
|
if (centerAnimation == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
SetValueInternal(TargetCenterProperty, center);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void TargetCenterPropertyChanged(Location targetCenter)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!internalPropertyChange && !targetCenter.Equals(Center))
|
|
|
|
|
|
{
|
2024-05-21 17:39:03 +02:00
|
|
|
|
ResetTransformCenter();
|
|
|
|
|
|
|
2024-05-21 13:51:10 +02:00
|
|
|
|
if (centerAnimation != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
centerAnimation.Completed -= CenterAnimationCompleted;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
centerAnimation = new LocationAnimation
|
|
|
|
|
|
{
|
2024-05-21 17:39:03 +02:00
|
|
|
|
To = new Location(targetCenter.Latitude, CoerceLongitude(targetCenter.Longitude)),
|
2024-05-21 13:51:10 +02:00
|
|
|
|
Duration = AnimationDuration,
|
2024-05-21 21:16:22 +02:00
|
|
|
|
EasingFunction = AnimationEasingFunction,
|
|
|
|
|
|
FillBehavior = FillBehavior.Stop
|
2024-05-21 13:51:10 +02:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
centerAnimation.Completed += CenterAnimationCompleted;
|
|
|
|
|
|
|
|
|
|
|
|
BeginAnimation(CenterProperty, centerAnimation);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void CenterAnimationCompleted(object sender, object e)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (centerAnimation != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
centerAnimation.Completed -= CenterAnimationCompleted;
|
|
|
|
|
|
centerAnimation = null;
|
|
|
|
|
|
|
|
|
|
|
|
BeginAnimation(CenterProperty, null);
|
2024-06-17 08:48:32 +02:00
|
|
|
|
SetValueInternal(CenterProperty, TargetCenter);
|
|
|
|
|
|
UpdateTransform();
|
2024-05-21 13:51:10 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void MinZoomLevelPropertyChanged(double minZoomLevel)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (ZoomLevel < minZoomLevel)
|
|
|
|
|
|
{
|
|
|
|
|
|
ZoomLevel = minZoomLevel;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void MaxZoomLevelPropertyChanged(double maxZoomLevel)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (ZoomLevel > maxZoomLevel)
|
|
|
|
|
|
{
|
|
|
|
|
|
ZoomLevel = maxZoomLevel;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void ZoomLevelPropertyChanged(double zoomLevel)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!internalPropertyChange)
|
|
|
|
|
|
{
|
|
|
|
|
|
UpdateTransform();
|
|
|
|
|
|
|
|
|
|
|
|
if (zoomLevelAnimation == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
SetValueInternal(TargetZoomLevelProperty, zoomLevel);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void TargetZoomLevelPropertyChanged(double targetZoomLevel)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!internalPropertyChange && targetZoomLevel != ZoomLevel)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (zoomLevelAnimation != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
zoomLevelAnimation.Completed -= ZoomLevelAnimationCompleted;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
zoomLevelAnimation = new DoubleAnimation
|
|
|
|
|
|
{
|
|
|
|
|
|
To = targetZoomLevel,
|
|
|
|
|
|
Duration = AnimationDuration,
|
2024-05-21 21:16:22 +02:00
|
|
|
|
EasingFunction = AnimationEasingFunction,
|
|
|
|
|
|
FillBehavior = FillBehavior.Stop
|
2024-05-21 13:51:10 +02:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
zoomLevelAnimation.Completed += ZoomLevelAnimationCompleted;
|
|
|
|
|
|
|
|
|
|
|
|
BeginAnimation(ZoomLevelProperty, zoomLevelAnimation);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void ZoomLevelAnimationCompleted(object sender, object e)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (zoomLevelAnimation != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
zoomLevelAnimation.Completed -= ZoomLevelAnimationCompleted;
|
|
|
|
|
|
zoomLevelAnimation = null;
|
|
|
|
|
|
|
|
|
|
|
|
BeginAnimation(ZoomLevelProperty, null);
|
2024-06-17 08:48:32 +02:00
|
|
|
|
SetValueInternal(ZoomLevelProperty, TargetZoomLevel);
|
|
|
|
|
|
UpdateTransform(true);
|
2024-05-21 13:51:10 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void HeadingPropertyChanged(double heading)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!internalPropertyChange)
|
|
|
|
|
|
{
|
|
|
|
|
|
UpdateTransform();
|
|
|
|
|
|
|
|
|
|
|
|
if (headingAnimation == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
SetValueInternal(TargetHeadingProperty, heading);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void TargetHeadingPropertyChanged(double targetHeading)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!internalPropertyChange && targetHeading != Heading)
|
|
|
|
|
|
{
|
|
|
|
|
|
var delta = targetHeading - Heading;
|
|
|
|
|
|
|
|
|
|
|
|
if (delta > 180d)
|
|
|
|
|
|
{
|
|
|
|
|
|
delta -= 360d;
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (delta < -180d)
|
|
|
|
|
|
{
|
|
|
|
|
|
delta += 360d;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (headingAnimation != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
headingAnimation.Completed -= HeadingAnimationCompleted;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
headingAnimation = new DoubleAnimation
|
|
|
|
|
|
{
|
|
|
|
|
|
By = delta,
|
|
|
|
|
|
Duration = AnimationDuration,
|
2024-05-21 21:16:22 +02:00
|
|
|
|
EasingFunction = AnimationEasingFunction,
|
|
|
|
|
|
FillBehavior = FillBehavior.Stop
|
2024-05-21 13:51:10 +02:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
headingAnimation.Completed += HeadingAnimationCompleted;
|
|
|
|
|
|
|
|
|
|
|
|
BeginAnimation(HeadingProperty, headingAnimation);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void HeadingAnimationCompleted(object sender, object e)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (headingAnimation != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
headingAnimation.Completed -= HeadingAnimationCompleted;
|
|
|
|
|
|
headingAnimation = null;
|
|
|
|
|
|
|
|
|
|
|
|
BeginAnimation(HeadingProperty, null);
|
2024-06-17 08:48:32 +02:00
|
|
|
|
SetValueInternal(HeadingProperty, TargetHeading);
|
|
|
|
|
|
UpdateTransform();
|
2024-05-21 13:51:10 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2012-11-22 21:42:29 +01:00
|
|
|
|
}
|
|
|
|
|
|
}
|