XAML-Map-Control/MapControl/Shared/MapBase.cs

354 lines
11 KiB
C#
Raw Normal View History

// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
2024-02-03 21:01:53 +01:00
// Copyright © 2024 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL)
using System;
2021-06-14 21:41:37 +02:00
#if WINUI
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Media.Animation;
2021-11-17 23:17:11 +01:00
#elif UWP
using Windows.UI.Xaml;
using Windows.UI.Xaml.Media.Animation;
#else
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Animation;
#endif
namespace MapControl
{
2024-05-20 23:24:34 +02:00
public partial class MapBase
{
public static readonly DependencyProperty MinZoomLevelProperty = DependencyProperty.Register(
nameof(MinZoomLevel), typeof(double), typeof(MapBase),
new PropertyMetadata(1d, (o, e) => ((MapBase)o).MinZoomLevelPropertyChanged((double)e.NewValue)));
public static readonly DependencyProperty MaxZoomLevelProperty = DependencyProperty.Register(
nameof(MaxZoomLevel), typeof(double), typeof(MapBase),
2020-03-25 16:14:20 +01:00
new PropertyMetadata(20d, (o, e) => ((MapBase)o).MaxZoomLevelPropertyChanged((double)e.NewValue)));
public static readonly DependencyProperty AnimationEasingFunctionProperty = DependencyProperty.Register(
nameof(AnimationEasingFunction), typeof(EasingFunctionBase), typeof(MapBase),
new PropertyMetadata(new QuadraticEase { EasingMode = EasingMode.EaseOut }));
private PointAnimation centerAnimation;
private DoubleAnimation zoomLevelAnimation;
private DoubleAnimation headingAnimation;
/// <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
{
2022-08-06 10:40:59 +02:00
get => (EasingFunctionBase)GetValue(AnimationEasingFunctionProperty);
set => SetValue(AnimationEasingFunctionProperty, value);
}
2022-11-02 19:49:18 +01:00
/// <summary>
2022-11-05 17:32:29 +01:00
/// Gets the scaling factor from projected map coordinates to view coordinates,
2022-11-07 21:10:10 +01:00
/// as pixels per meter.
2022-11-02 19:49:18 +01:00
/// </summary>
public double ViewScale => (double)GetValue(ViewScaleProperty);
/// <summary>
/// Gets a transform Matrix for scaling and rotating objects that are anchored
/// at a Location from map coordinates (i.e. meters) to view coordinates.
/// </summary>
public Matrix GetMapTransform(Location location)
{
var scale = GetScale(location);
2024-05-19 23:22:54 +02:00
var transform = new Matrix(scale.X, 0d, 0d, scale.Y, 0d, 0d);
transform.Rotate(ViewTransform.Rotation);
return transform;
}
2022-03-05 18:40:57 +01:00
private void MapProjectionPropertyChanged(MapProjection projection)
{
2022-03-05 18:40:57 +01:00
maxLatitude = 90d;
if (projection.Type <= MapProjectionType.NormalCylindrical)
{
var maxLocation = projection.MapToLocation(new Point(0d, 180d * MapProjection.Wgs84MeterPerDegree));
if (maxLocation != null && maxLocation.Latitude < 90d)
{
maxLatitude = maxLocation.Latitude;
2024-05-20 08:33:30 +02:00
CoerceCenterProperty(CenterProperty, Center);
2022-03-05 18:40:57 +01:00
}
}
ResetTransformCenter();
UpdateTransform(false, true);
}
2024-05-20 08:33:30 +02:00
private Location CoerceCenterProperty(DependencyProperty property, Location center)
{
var c = center;
if (center == null)
{
center = new Location();
}
else if (
center.Latitude < -maxLatitude || center.Latitude > maxLatitude ||
center.Longitude < -180d || center.Longitude > 180d)
{
center = new Location(
Math.Min(Math.Max(center.Latitude, -maxLatitude), maxLatitude),
Location.NormalizeLongitude(center.Longitude));
}
if (center != c)
{
SetValueInternal(property, center);
}
return center;
}
private void CenterPropertyChanged(Location center)
{
if (!internalPropertyChange)
{
2024-05-20 08:33:30 +02:00
center = CoerceCenterProperty(CenterProperty, center);
UpdateTransform();
if (centerAnimation == null)
{
2017-11-10 17:26:15 +01:00
SetValueInternal(TargetCenterProperty, center);
}
}
}
private void TargetCenterPropertyChanged(Location targetCenter)
{
if (!internalPropertyChange)
{
2024-05-20 08:33:30 +02:00
targetCenter = CoerceCenterProperty(TargetCenterProperty, targetCenter);
if (!targetCenter.Equals(Center))
{
if (centerAnimation != null)
{
centerAnimation.Completed -= CenterAnimationCompleted;
}
centerAnimation = new PointAnimation
{
2017-11-10 17:26:15 +01:00
From = new Point(Center.Longitude, Center.Latitude),
2020-05-13 18:17:28 +02:00
To = new Point(ConstrainedLongitude(targetCenter.Longitude), targetCenter.Latitude),
Duration = AnimationDuration,
2017-11-10 17:26:15 +01:00
EasingFunction = AnimationEasingFunction
};
centerAnimation.Completed += CenterAnimationCompleted;
2017-11-10 17:26:15 +01:00
this.BeginAnimation(CenterPointProperty, centerAnimation);
}
}
}
private void CenterAnimationCompleted(object sender, object e)
{
if (centerAnimation != null)
{
centerAnimation.Completed -= CenterAnimationCompleted;
centerAnimation = null;
2017-11-10 17:26:15 +01:00
this.BeginAnimation(CenterPointProperty, null);
}
}
private void CenterPointPropertyChanged(Location center)
{
2017-11-10 17:26:15 +01:00
if (centerAnimation != null)
{
SetValueInternal(CenterProperty, center);
UpdateTransform();
}
}
private void MinZoomLevelPropertyChanged(double minZoomLevel)
{
if (minZoomLevel < 0d || minZoomLevel > MaxZoomLevel)
{
minZoomLevel = Math.Min(Math.Max(minZoomLevel, 0d), MaxZoomLevel);
2021-01-07 22:52:41 +01:00
2017-11-10 17:26:15 +01:00
SetValueInternal(MinZoomLevelProperty, minZoomLevel);
}
if (ZoomLevel < minZoomLevel)
{
ZoomLevel = minZoomLevel;
}
}
private void MaxZoomLevelPropertyChanged(double maxZoomLevel)
{
2021-01-07 22:52:41 +01:00
if (maxZoomLevel < MinZoomLevel)
{
2021-01-07 22:52:41 +01:00
maxZoomLevel = MinZoomLevel;
2017-11-10 17:26:15 +01:00
SetValueInternal(MaxZoomLevelProperty, maxZoomLevel);
}
if (ZoomLevel > maxZoomLevel)
{
ZoomLevel = maxZoomLevel;
}
}
2024-05-20 08:33:30 +02:00
private double CoerceZoomLevelProperty(DependencyProperty property, double zoomLevel)
{
if (zoomLevel < MinZoomLevel || zoomLevel > MaxZoomLevel)
{
zoomLevel = Math.Min(Math.Max(zoomLevel, MinZoomLevel), MaxZoomLevel);
SetValueInternal(property, zoomLevel);
}
return zoomLevel;
}
private void ZoomLevelPropertyChanged(double zoomLevel)
{
if (!internalPropertyChange)
{
2024-05-20 08:33:30 +02:00
zoomLevel = CoerceZoomLevelProperty(ZoomLevelProperty, zoomLevel);
UpdateTransform();
if (zoomLevelAnimation == null)
{
2017-11-10 17:26:15 +01:00
SetValueInternal(TargetZoomLevelProperty, zoomLevel);
}
}
}
private void TargetZoomLevelPropertyChanged(double targetZoomLevel)
{
if (!internalPropertyChange)
{
2024-05-20 08:33:30 +02:00
targetZoomLevel = CoerceZoomLevelProperty(TargetZoomLevelProperty, targetZoomLevel);
if (targetZoomLevel != ZoomLevel)
{
if (zoomLevelAnimation != null)
{
zoomLevelAnimation.Completed -= ZoomLevelAnimationCompleted;
}
zoomLevelAnimation = new DoubleAnimation
{
To = targetZoomLevel,
Duration = AnimationDuration,
2017-11-10 17:26:15 +01:00
EasingFunction = AnimationEasingFunction
};
zoomLevelAnimation.Completed += ZoomLevelAnimationCompleted;
2017-11-10 17:26:15 +01:00
this.BeginAnimation(ZoomLevelProperty, zoomLevelAnimation);
}
}
}
private void ZoomLevelAnimationCompleted(object sender, object e)
{
if (zoomLevelAnimation != null)
{
2017-11-10 17:26:15 +01:00
SetValueInternal(ZoomLevelProperty, TargetZoomLevel);
UpdateTransform(true);
zoomLevelAnimation.Completed -= ZoomLevelAnimationCompleted;
zoomLevelAnimation = null;
2017-11-10 17:26:15 +01:00
this.BeginAnimation(ZoomLevelProperty, null);
}
}
2024-05-20 08:33:30 +02:00
private double CoerceHeadingProperty(DependencyProperty property, double heading)
{
if (heading < 0d || heading > 360d)
{
heading = ((heading % 360d) + 360d) % 360d;
SetValueInternal(property, heading);
}
return heading;
}
private void HeadingPropertyChanged(double heading)
{
if (!internalPropertyChange)
{
2024-05-20 08:33:30 +02:00
heading = CoerceHeadingProperty(HeadingProperty, heading);
UpdateTransform();
if (headingAnimation == null)
{
2017-11-10 17:26:15 +01:00
SetValueInternal(TargetHeadingProperty, heading);
}
}
}
private void TargetHeadingPropertyChanged(double targetHeading)
{
if (!internalPropertyChange)
{
2024-05-20 08:33:30 +02:00
targetHeading = CoerceHeadingProperty(TargetHeadingProperty, targetHeading);
if (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,
2017-11-10 17:26:15 +01:00
EasingFunction = AnimationEasingFunction
};
headingAnimation.Completed += HeadingAnimationCompleted;
2017-11-10 17:26:15 +01:00
this.BeginAnimation(HeadingProperty, headingAnimation);
}
}
}
private void HeadingAnimationCompleted(object sender, object e)
{
if (headingAnimation != null)
{
2017-11-10 17:26:15 +01:00
SetValueInternal(HeadingProperty, TargetHeading);
UpdateTransform();
headingAnimation.Completed -= HeadingAnimationCompleted;
headingAnimation = null;
2017-11-10 17:26:15 +01:00
this.BeginAnimation(HeadingProperty, null);
}
}
}
}