2017-06-25 23:05:48 +02:00
|
|
|
|
// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
|
2022-01-14 20:22:56 +01:00
|
|
|
|
// © 2022 Clemens Fischer
|
2012-10-12 19:22:49 +02:00
|
|
|
|
// Licensed under the Microsoft Public License (Ms-PL)
|
|
|
|
|
|
|
|
|
|
|
|
using System;
|
2021-06-14 21:41:37 +02:00
|
|
|
|
#if WINUI
|
|
|
|
|
|
using Windows.Foundation;
|
|
|
|
|
|
using Microsoft.UI.Xaml;
|
|
|
|
|
|
using Microsoft.UI.Xaml.Media;
|
|
|
|
|
|
using Microsoft.UI.Xaml.Media.Animation;
|
2021-11-17 23:17:11 +01:00
|
|
|
|
#elif UWP
|
2020-03-26 19:08:20 +01:00
|
|
|
|
using Windows.Foundation;
|
2012-11-22 21:42:29 +01:00
|
|
|
|
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
|
|
|
|
|
|
{
|
2017-06-25 23:05:48 +02:00
|
|
|
|
public interface IMapLayer : IMapElement
|
|
|
|
|
|
{
|
|
|
|
|
|
Brush MapBackground { get; }
|
|
|
|
|
|
Brush MapForeground { get; }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2012-10-12 19:22:49 +02:00
|
|
|
|
/// <summary>
|
2020-03-26 19:08:20 +01:00
|
|
|
|
/// The map control. Displays map content provided by one or more tile or image layers,
|
2022-11-07 21:10:10 +01:00
|
|
|
|
/// such as MapTileLayerBase or MapImageLayer instances.
|
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
|
|
|
|
{
|
2020-04-22 16:16:17 +02:00
|
|
|
|
public static TimeSpan ImageFadeDuration { get; set; } = TimeSpan.FromSeconds(0.1);
|
2020-04-19 10:53:25 +02:00
|
|
|
|
|
2017-06-25 23:05:48 +02:00
|
|
|
|
public static readonly DependencyProperty MapLayerProperty = DependencyProperty.Register(
|
|
|
|
|
|
nameof(MapLayer), typeof(UIElement), typeof(MapBase),
|
|
|
|
|
|
new PropertyMetadata(null, (o, e) => ((MapBase)o).MapLayerPropertyChanged((UIElement)e.OldValue, (UIElement)e.NewValue)));
|
|
|
|
|
|
|
|
|
|
|
|
public static readonly DependencyProperty MapProjectionProperty = DependencyProperty.Register(
|
|
|
|
|
|
nameof(MapProjection), typeof(MapProjection), typeof(MapBase),
|
2022-03-05 18:40:57 +01:00
|
|
|
|
new PropertyMetadata(new WebMercatorProjection(), (o, e) => ((MapBase)o).MapProjectionPropertyChanged((MapProjection)e.NewValue)));
|
2012-10-12 19:22:49 +02:00
|
|
|
|
|
2017-06-25 23:05:48 +02:00
|
|
|
|
public static readonly DependencyProperty ProjectionCenterProperty = DependencyProperty.Register(
|
|
|
|
|
|
nameof(ProjectionCenter), typeof(Location), typeof(MapBase),
|
|
|
|
|
|
new PropertyMetadata(null, (o, e) => ((MapBase)o).ProjectionCenterPropertyChanged()));
|
2012-10-12 19:22:49 +02:00
|
|
|
|
|
2012-11-22 21:42:29 +01:00
|
|
|
|
public static readonly DependencyProperty MinZoomLevelProperty = DependencyProperty.Register(
|
2017-06-25 23:05:48 +02:00
|
|
|
|
nameof(MinZoomLevel), typeof(double), typeof(MapBase),
|
2015-11-11 19:48:50 +01:00
|
|
|
|
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(
|
2017-06-25 23:05:48 +02:00
|
|
|
|
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)));
|
2015-11-11 19:48:50 +01:00
|
|
|
|
|
|
|
|
|
|
public static readonly DependencyProperty AnimationDurationProperty = DependencyProperty.Register(
|
2017-06-25 23:05:48 +02:00
|
|
|
|
nameof(AnimationDuration), typeof(TimeSpan), typeof(MapBase),
|
2015-11-11 19:48:50 +01:00
|
|
|
|
new PropertyMetadata(TimeSpan.FromSeconds(0.3)));
|
|
|
|
|
|
|
|
|
|
|
|
public static readonly DependencyProperty AnimationEasingFunctionProperty = DependencyProperty.Register(
|
2017-06-25 23:05:48 +02:00
|
|
|
|
nameof(AnimationEasingFunction), typeof(EasingFunctionBase), typeof(MapBase),
|
2015-11-11 19:48:50 +01:00
|
|
|
|
new PropertyMetadata(new QuadraticEase { EasingMode = EasingMode.EaseOut }));
|
|
|
|
|
|
|
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;
|
2017-06-25 23:05:48 +02:00
|
|
|
|
private Location transformCenter;
|
2020-03-28 21:53:38 +01:00
|
|
|
|
private Point viewCenter;
|
2017-06-25 23:05:48 +02:00
|
|
|
|
private double centerLongitude;
|
2022-03-05 18:40:57 +01:00
|
|
|
|
private double maxLatitude = 90d;
|
2012-11-22 21:42:29 +01:00
|
|
|
|
private bool internalPropertyChange;
|
2012-10-12 19:22:49 +02:00
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2020-03-28 21:53:38 +01:00
|
|
|
|
/// Raised when the current map viewport has changed.
|
2012-10-12 19:22:49 +02:00
|
|
|
|
/// </summary>
|
2017-02-05 18:35:30 +01:00
|
|
|
|
public event EventHandler<ViewportChangedEventArgs> ViewportChanged;
|
2012-10-12 19:22:49 +02:00
|
|
|
|
|
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
|
|
|
|
|
|
{
|
2022-08-06 10:40:59 +02:00
|
|
|
|
get => (Brush)GetValue(ForegroundProperty);
|
|
|
|
|
|
set => SetValue(ForegroundProperty, value);
|
2012-10-12 19:22:49 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2017-06-25 23:05:48 +02:00
|
|
|
|
/// Gets or sets the base map layer, which is added as first element to the Children collection.
|
|
|
|
|
|
/// If the layer implements IMapLayer (like MapTileLayer or MapImageLayer), its (non-null) MapBackground
|
|
|
|
|
|
/// and MapForeground property values are used for the MapBase Background and Foreground properties.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public UIElement MapLayer
|
|
|
|
|
|
{
|
2022-08-06 10:40:59 +02:00
|
|
|
|
get => (UIElement)GetValue(MapLayerProperty);
|
|
|
|
|
|
set => SetValue(MapLayerProperty, value);
|
2017-06-25 23:05:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Gets or sets the MapProjection used by the map control.
|
2012-10-12 19:22:49 +02:00
|
|
|
|
/// </summary>
|
2017-06-25 23:05:48 +02:00
|
|
|
|
public MapProjection MapProjection
|
2012-10-12 19:22:49 +02:00
|
|
|
|
{
|
2022-08-06 10:40:59 +02:00
|
|
|
|
get => (MapProjection)GetValue(MapProjectionProperty);
|
|
|
|
|
|
set => SetValue(MapProjectionProperty, value);
|
2012-10-12 19:22:49 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2017-06-25 23:05:48 +02:00
|
|
|
|
/// Gets or sets an optional center (reference point) for azimuthal projections.
|
|
|
|
|
|
/// If ProjectionCenter is null, the Center property value will be used instead.
|
2012-10-12 19:22:49 +02:00
|
|
|
|
/// </summary>
|
2017-06-25 23:05:48 +02:00
|
|
|
|
public Location ProjectionCenter
|
2012-10-12 19:22:49 +02:00
|
|
|
|
{
|
2022-08-06 10:40:59 +02:00
|
|
|
|
get => (Location)GetValue(ProjectionCenterProperty);
|
|
|
|
|
|
set => SetValue(ProjectionCenterProperty, value);
|
2012-10-12 19:22:49 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2017-06-25 23:05:48 +02:00
|
|
|
|
/// Gets or sets the location of the center point of the map.
|
2012-10-12 19:22:49 +02:00
|
|
|
|
/// </summary>
|
|
|
|
|
|
public Location Center
|
|
|
|
|
|
{
|
2022-08-06 10:40:59 +02:00
|
|
|
|
get => (Location)GetValue(CenterProperty);
|
|
|
|
|
|
set => SetValue(CenterProperty, value);
|
2012-10-12 19:22:49 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Gets or sets the target value of a Center animation.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public Location TargetCenter
|
|
|
|
|
|
{
|
2022-08-06 10:40:59 +02:00
|
|
|
|
get => (Location)GetValue(TargetCenterProperty);
|
|
|
|
|
|
set => SetValue(TargetCenterProperty, value);
|
2012-10-12 19:22:49 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
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
|
|
|
|
|
|
{
|
2022-08-06 10:40:59 +02:00
|
|
|
|
get => (double)GetValue(MinZoomLevelProperty);
|
|
|
|
|
|
set => SetValue(MinZoomLevelProperty, value);
|
2012-11-22 21:42:29 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Gets or sets the maximum value of the ZoomLevel and TargetZommLevel properties.
|
2020-03-25 16:14:20 +01:00
|
|
|
|
/// Must be greater than or equal to MinZoomLevel and less than or equal to 22.
|
|
|
|
|
|
/// The default value is 20.
|
2012-11-22 21:42:29 +01:00
|
|
|
|
/// </summary>
|
|
|
|
|
|
public double MaxZoomLevel
|
|
|
|
|
|
{
|
2022-08-06 10:40:59 +02:00
|
|
|
|
get => (double)GetValue(MaxZoomLevelProperty);
|
|
|
|
|
|
set => SetValue(MaxZoomLevelProperty, value);
|
2012-11-22 21:42:29 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2012-10-12 19:22:49 +02:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Gets or sets the map zoom level.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public double ZoomLevel
|
|
|
|
|
|
{
|
2022-08-06 10:40:59 +02:00
|
|
|
|
get => (double)GetValue(ZoomLevelProperty);
|
|
|
|
|
|
set => SetValue(ZoomLevelProperty, value);
|
2012-10-12 19:22:49 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Gets or sets the target value of a ZoomLevel animation.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public double TargetZoomLevel
|
|
|
|
|
|
{
|
2022-08-06 10:40:59 +02:00
|
|
|
|
get => (double)GetValue(TargetZoomLevelProperty);
|
|
|
|
|
|
set => SetValue(TargetZoomLevelProperty, value);
|
2012-10-12 19:22:49 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2022-11-07 21:10:10 +01:00
|
|
|
|
/// Gets or sets the map heading, a counter-clockwise rotation angle in degrees.
|
2012-10-12 19:22:49 +02:00
|
|
|
|
/// </summary>
|
|
|
|
|
|
public double Heading
|
|
|
|
|
|
{
|
2022-08-06 10:40:59 +02:00
|
|
|
|
get => (double)GetValue(HeadingProperty);
|
|
|
|
|
|
set => SetValue(HeadingProperty, value);
|
2012-10-12 19:22:49 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Gets or sets the target value of a Heading animation.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public double TargetHeading
|
|
|
|
|
|
{
|
2022-08-06 10:40:59 +02:00
|
|
|
|
get => (double)GetValue(TargetHeadingProperty);
|
|
|
|
|
|
set => SetValue(TargetHeadingProperty, value);
|
2012-10-12 19:22:49 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
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
|
|
|
|
|
|
{
|
2022-08-06 10:40:59 +02:00
|
|
|
|
get => (TimeSpan)GetValue(AnimationDurationProperty);
|
|
|
|
|
|
set => SetValue(AnimationDurationProperty, value);
|
2015-11-11 19:48:50 +01: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 EasingFunctionBase AnimationEasingFunction
|
|
|
|
|
|
{
|
2022-08-06 10:40:59 +02:00
|
|
|
|
get => (EasingFunctionBase)GetValue(AnimationEasingFunctionProperty);
|
|
|
|
|
|
set => SetValue(AnimationEasingFunctionProperty, value);
|
2015-11-11 19:48:50 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
2018-04-10 22:34:34 +02:00
|
|
|
|
/// <summary>
|
2022-11-05 17:32:29 +01:00
|
|
|
|
/// Gets the ViewTransform instance that is used to transform between projected
|
|
|
|
|
|
/// map coordinates and view coordinates.
|
2012-10-12 19:22:49 +02:00
|
|
|
|
/// </summary>
|
2020-03-28 21:53:38 +01:00
|
|
|
|
public ViewTransform ViewTransform { get; } = new ViewTransform();
|
2012-10-12 19:22:49 +02:00
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2022-11-07 21:10:10 +01:00
|
|
|
|
/// Gets the map scale as the horizontal and vertical scaling factors from geographic
|
|
|
|
|
|
/// coordinates to view coordinates at the specified location, as pixels per meter.
|
2012-10-12 19:22:49 +02:00
|
|
|
|
/// </summary>
|
2020-03-28 21:53:38 +01:00
|
|
|
|
public Vector GetScale(Location location)
|
2020-03-26 19:08:20 +01:00
|
|
|
|
{
|
2020-03-28 21:53:38 +01:00
|
|
|
|
return ViewTransform.Scale * MapProjection.GetRelativeScale(location);
|
2020-03-26 19:08:20 +01:00
|
|
|
|
}
|
2012-11-22 21:42:29 +01:00
|
|
|
|
|
2012-10-12 19:22:49 +02:00
|
|
|
|
/// <summary>
|
2020-03-28 21:53:38 +01:00
|
|
|
|
/// Transforms a Location in geographic coordinates to a Point in view coordinates.
|
2012-10-12 19:22:49 +02:00
|
|
|
|
/// </summary>
|
2020-03-28 21:53:38 +01:00
|
|
|
|
public Point LocationToView(Location location)
|
2012-10-12 19:22:49 +02:00
|
|
|
|
{
|
2020-03-26 19:08:20 +01:00
|
|
|
|
return ViewTransform.MapToView(MapProjection.LocationToMap(location));
|
2012-10-12 19:22:49 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2020-03-28 21:53:38 +01:00
|
|
|
|
/// Transforms a Point in view coordinates to a Location in geographic coordinates.
|
2012-10-12 19:22:49 +02:00
|
|
|
|
/// </summary>
|
2020-03-28 21:53:38 +01:00
|
|
|
|
public Location ViewToLocation(Point point)
|
2012-10-12 19:22:49 +02:00
|
|
|
|
{
|
2020-03-26 19:08:20 +01:00
|
|
|
|
return MapProjection.MapToLocation(ViewTransform.ViewToMap(point));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2020-03-28 21:53:38 +01:00
|
|
|
|
/// Transforms a Rect in view coordinates to a BoundingBox in geographic coordinates.
|
2020-03-26 19:08:20 +01:00
|
|
|
|
/// </summary>
|
2020-03-28 21:53:38 +01:00
|
|
|
|
public BoundingBox ViewRectToBoundingBox(Rect rect)
|
2020-03-26 19:08:20 +01:00
|
|
|
|
{
|
|
|
|
|
|
var p1 = ViewTransform.ViewToMap(new Point(rect.X, rect.Y));
|
|
|
|
|
|
var p2 = ViewTransform.ViewToMap(new Point(rect.X, rect.Y + rect.Height));
|
|
|
|
|
|
var p3 = ViewTransform.ViewToMap(new Point(rect.X + rect.Width, rect.Y));
|
|
|
|
|
|
var p4 = ViewTransform.ViewToMap(new Point(rect.X + rect.Width, rect.Y + rect.Height));
|
|
|
|
|
|
|
2022-11-30 17:59:38 +01:00
|
|
|
|
var x1 = Math.Min(p1.X, Math.Min(p2.X, Math.Min(p3.X, p4.X)));
|
|
|
|
|
|
var x2 = Math.Max(p1.X, Math.Max(p2.X, Math.Max(p3.X, p4.X)));
|
|
|
|
|
|
var y1 = Math.Min(p1.Y, Math.Min(p2.Y, Math.Min(p3.Y, p4.Y)));
|
|
|
|
|
|
var y2 = Math.Max(p1.Y, Math.Max(p2.Y, Math.Max(p3.Y, p4.Y)));
|
2020-03-26 19:08:20 +01:00
|
|
|
|
|
2022-11-30 17:59:38 +01:00
|
|
|
|
return MapProjection.RectToBoundingBox(new Rect(x1, y1, x2 - x1, y2 - y1));
|
2012-10-12 19:22:49 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2020-03-28 21:53:38 +01:00
|
|
|
|
/// Sets a temporary center point in view coordinates for scaling and rotation transformations.
|
2022-11-24 23:24:51 +01:00
|
|
|
|
/// This center point is automatically reset when the Center property is set by application code
|
|
|
|
|
|
/// or by the methods TranslateMap, TransformMap, ZoomMap and ZoomToBounds.
|
2012-10-12 19:22:49 +02:00
|
|
|
|
/// </summary>
|
2017-06-25 23:05:48 +02:00
|
|
|
|
public void SetTransformCenter(Point center)
|
2012-10-12 19:22:49 +02:00
|
|
|
|
{
|
2020-03-28 21:53:38 +01:00
|
|
|
|
transformCenter = ViewToLocation(center);
|
2022-03-04 22:28:18 +01:00
|
|
|
|
viewCenter = transformCenter != null ? center : new Point(RenderSize.Width / 2d, RenderSize.Height / 2d);
|
2012-10-12 19:22:49 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2017-06-25 23:05:48 +02:00
|
|
|
|
/// Resets the temporary transform center point set by SetTransformCenter.
|
2012-10-12 19:22:49 +02:00
|
|
|
|
/// </summary>
|
2017-06-25 23:05:48 +02:00
|
|
|
|
public void ResetTransformCenter()
|
2012-10-12 19:22:49 +02:00
|
|
|
|
{
|
2017-06-25 23:05:48 +02:00
|
|
|
|
transformCenter = null;
|
2020-03-28 21:53:38 +01:00
|
|
|
|
viewCenter = new Point(RenderSize.Width / 2d, RenderSize.Height / 2d);
|
2012-10-12 19:22:49 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2020-03-28 21:53:38 +01:00
|
|
|
|
/// Changes the Center property according to the specified translation in view coordinates.
|
2012-10-12 19:22:49 +02:00
|
|
|
|
/// </summary>
|
2018-03-07 22:19:16 +01:00
|
|
|
|
public void TranslateMap(Vector translation)
|
2012-10-12 19:22:49 +02:00
|
|
|
|
{
|
2017-06-25 23:05:48 +02:00
|
|
|
|
if (transformCenter != null)
|
2013-11-22 18:46:45 +01:00
|
|
|
|
{
|
2017-06-25 23:05:48 +02:00
|
|
|
|
ResetTransformCenter();
|
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)
|
|
|
|
|
|
{
|
2022-03-04 22:28:18 +01:00
|
|
|
|
var center = ViewToLocation(viewCenter - translation);
|
|
|
|
|
|
if (center != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
Center = center;
|
|
|
|
|
|
}
|
2012-10-12 19:22:49 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Changes the Center, Heading and ZoomLevel properties according to the specified
|
2020-03-28 21:53:38 +01:00
|
|
|
|
/// view coordinate translation, rotation and scale delta values. Rotation and scaling
|
|
|
|
|
|
/// is performed relative to the specified center point in view coordinates.
|
2012-10-12 19:22:49 +02:00
|
|
|
|
/// </summary>
|
2018-03-07 22:19:16 +01:00
|
|
|
|
public void TransformMap(Point center, Vector 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)
|
|
|
|
|
|
{
|
2022-03-04 22:28:18 +01:00
|
|
|
|
SetTransformCenter(center);
|
|
|
|
|
|
viewCenter += translation;
|
2013-04-04 18:01:13 +02:00
|
|
|
|
|
2016-01-30 11:50:53 +01:00
|
|
|
|
if (rotation != 0d)
|
|
|
|
|
|
{
|
2022-11-07 21:10:10 +01:00
|
|
|
|
var heading = (((Heading - rotation) % 360d) + 360d) % 360d;
|
2021-01-07 22:52:41 +01:00
|
|
|
|
|
2017-11-10 17:26:15 +01:00
|
|
|
|
SetValueInternal(HeadingProperty, heading);
|
|
|
|
|
|
SetValueInternal(TargetHeadingProperty, heading);
|
2016-01-30 11:50:53 +01:00
|
|
|
|
}
|
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);
|
2021-01-07 22:52:41 +01:00
|
|
|
|
|
2017-11-10 17:26:15 +01:00
|
|
|
|
SetValueInternal(ZoomLevelProperty, zoomLevel);
|
|
|
|
|
|
SetValueInternal(TargetZoomLevelProperty, zoomLevel);
|
2016-01-30 11:50:53 +01:00
|
|
|
|
}
|
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>
|
2017-06-25 23:05:48 +02:00
|
|
|
|
/// Sets the value of the TargetZoomLevel property while retaining the specified center point
|
2020-03-28 21:53:38 +01:00
|
|
|
|
/// in view coordinates.
|
2012-10-12 19:22:49 +02:00
|
|
|
|
/// </summary>
|
2017-06-25 23:05:48 +02:00
|
|
|
|
public void ZoomMap(Point center, double zoomLevel)
|
2012-10-12 19:22:49 +02:00
|
|
|
|
{
|
2016-02-21 21:39:37 +01:00
|
|
|
|
zoomLevel = Math.Min(Math.Max(zoomLevel, MinZoomLevel), MaxZoomLevel);
|
|
|
|
|
|
|
|
|
|
|
|
if (TargetZoomLevel != zoomLevel)
|
|
|
|
|
|
{
|
2017-06-25 23:05:48 +02:00
|
|
|
|
SetTransformCenter(center);
|
2018-08-25 17:54:09 +02:00
|
|
|
|
TargetZoomLevel = zoomLevel;
|
2016-02-21 21:39:37 +01:00
|
|
|
|
}
|
2012-10-12 19:22:49 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2013-11-17 16:52:03 +01:00
|
|
|
|
/// <summary>
|
2017-06-25 23:05:48 +02:00
|
|
|
|
/// Sets the TargetZoomLevel and TargetCenter properties so that the specified bounding box
|
2020-03-28 21:53:38 +01:00
|
|
|
|
/// fits into the current view. The TargetHeading property is set to zero.
|
2013-11-17 16:52:03 +01:00
|
|
|
|
/// </summary>
|
2017-06-25 23:05:48 +02:00
|
|
|
|
public void ZoomToBounds(BoundingBox boundingBox)
|
2013-11-17 16:52:03 +01:00
|
|
|
|
{
|
2018-12-20 21:55:12 +01:00
|
|
|
|
var rect = MapProjection.BoundingBoxToRect(boundingBox);
|
|
|
|
|
|
var center = new Point(rect.X + rect.Width / 2d, rect.Y + rect.Height / 2d);
|
2022-03-04 22:28:18 +01:00
|
|
|
|
var targetCenter = MapProjection.MapToLocation(center);
|
|
|
|
|
|
|
|
|
|
|
|
if (targetCenter != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
var scale = Math.Min(RenderSize.Width / rect.Width, RenderSize.Height / rect.Height);
|
2018-12-20 21:55:12 +01:00
|
|
|
|
|
2022-03-04 22:28:18 +01:00
|
|
|
|
TargetZoomLevel = ViewTransform.ScaleToZoomLevel(scale);
|
|
|
|
|
|
TargetCenter = targetCenter;
|
|
|
|
|
|
TargetHeading = 0d;
|
|
|
|
|
|
}
|
2013-11-17 16:52:03 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2020-05-13 18:17:28 +02:00
|
|
|
|
internal double ConstrainedLongitude(double longitude)
|
|
|
|
|
|
{
|
|
|
|
|
|
var offset = longitude - Center.Longitude;
|
|
|
|
|
|
|
|
|
|
|
|
if (offset > 180d)
|
|
|
|
|
|
{
|
|
|
|
|
|
longitude = Center.Longitude - 360d + offset % 360d;
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (offset < -180d)
|
|
|
|
|
|
{
|
|
|
|
|
|
longitude = Center.Longitude + 360d + offset % 360d;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return longitude;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-06-25 23:05:48 +02:00
|
|
|
|
private void MapLayerPropertyChanged(UIElement oldLayer, UIElement newLayer)
|
2012-10-12 19:22:49 +02:00
|
|
|
|
{
|
2017-06-25 23:05:48 +02:00
|
|
|
|
if (oldLayer != null)
|
2014-11-19 21:11:14 +01:00
|
|
|
|
{
|
2017-06-25 23:05:48 +02:00
|
|
|
|
Children.Remove(oldLayer);
|
2014-11-20 17:13:11 +01:00
|
|
|
|
|
2020-04-16 23:15:03 +02:00
|
|
|
|
if (oldLayer is IMapLayer mapLayer)
|
2014-12-17 21:38:28 +01:00
|
|
|
|
{
|
2017-06-25 23:05:48 +02:00
|
|
|
|
if (mapLayer.MapBackground != null)
|
2012-10-12 19:22:49 +02:00
|
|
|
|
{
|
2017-06-25 23:05:48 +02:00
|
|
|
|
ClearValue(BackgroundProperty);
|
2012-10-12 19:22:49 +02:00
|
|
|
|
}
|
2017-06-25 23:05:48 +02:00
|
|
|
|
if (mapLayer.MapForeground != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
ClearValue(ForegroundProperty);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2012-10-12 19:22:49 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2017-06-25 23:05:48 +02:00
|
|
|
|
if (newLayer != null)
|
2014-11-20 17:13:11 +01:00
|
|
|
|
{
|
2017-06-25 23:05:48 +02:00
|
|
|
|
Children.Insert(0, newLayer);
|
2012-10-12 19:22:49 +02:00
|
|
|
|
|
2020-04-16 23:15:03 +02:00
|
|
|
|
if (newLayer is IMapLayer mapLayer)
|
2014-07-22 20:02:30 +02:00
|
|
|
|
{
|
2017-06-25 23:05:48 +02:00
|
|
|
|
if (mapLayer.MapBackground != null)
|
2014-11-19 21:11:14 +01:00
|
|
|
|
{
|
2017-06-25 23:05:48 +02:00
|
|
|
|
Background = mapLayer.MapBackground;
|
2014-11-19 21:11:14 +01:00
|
|
|
|
}
|
2017-06-25 23:05:48 +02:00
|
|
|
|
if (mapLayer.MapForeground != null)
|
2014-11-19 21:11:14 +01:00
|
|
|
|
{
|
2017-06-25 23:05:48 +02:00
|
|
|
|
Foreground = mapLayer.MapForeground;
|
2014-11-19 21:11:14 +01:00
|
|
|
|
}
|
2012-11-22 21:42:29 +01:00
|
|
|
|
}
|
2012-10-12 19:22:49 +02:00
|
|
|
|
}
|
2014-11-19 21:11:14 +01:00
|
|
|
|
}
|
2012-10-12 19:22:49 +02:00
|
|
|
|
|
2022-03-05 18:40:57 +01:00
|
|
|
|
private void MapProjectionPropertyChanged(MapProjection projection)
|
2014-11-19 21:11:14 +01:00
|
|
|
|
{
|
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;
|
|
|
|
|
|
|
|
|
|
|
|
var center = Center;
|
|
|
|
|
|
AdjustCenterProperty(CenterProperty, ref center);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-06-25 23:05:48 +02:00
|
|
|
|
ResetTransformCenter();
|
|
|
|
|
|
UpdateTransform(false, true);
|
2015-06-09 20:09:57 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2017-06-25 23:05:48 +02:00
|
|
|
|
private void ProjectionCenterPropertyChanged()
|
2012-11-22 21:42:29 +01:00
|
|
|
|
{
|
2018-08-25 17:54:09 +02:00
|
|
|
|
ResetTransformCenter();
|
|
|
|
|
|
UpdateTransform();
|
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
|
|
|
|
{
|
2022-03-05 18:40:57 +01:00
|
|
|
|
var c = center;
|
|
|
|
|
|
|
|
|
|
|
|
if (center == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
center = new Location();
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (
|
|
|
|
|
|
center.Latitude < -maxLatitude || center.Latitude > maxLatitude ||
|
|
|
|
|
|
center.Longitude < -180d || center.Longitude > 180d)
|
2013-04-04 18:01:13 +02:00
|
|
|
|
{
|
2022-03-05 18:40:57 +01:00
|
|
|
|
center = new Location(
|
|
|
|
|
|
Math.Min(Math.Max(center.Latitude, -maxLatitude), maxLatitude),
|
|
|
|
|
|
Location.NormalizeLongitude(center.Longitude));
|
|
|
|
|
|
}
|
2021-01-07 22:52:41 +01:00
|
|
|
|
|
2022-03-05 18:40:57 +01:00
|
|
|
|
if (center != c)
|
|
|
|
|
|
{
|
2017-11-10 17:26:15 +01:00
|
|
|
|
SetValueInternal(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)
|
|
|
|
|
|
{
|
2017-11-10 17:26:15 +01:00
|
|
|
|
SetValueInternal(TargetCenterProperty, 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;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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),
|
2014-11-19 21:11:14 +01:00
|
|
|
|
Duration = AnimationDuration,
|
2017-11-10 17:26:15 +01:00
|
|
|
|
EasingFunction = AnimationEasingFunction
|
2012-11-22 21:42:29 +01:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
centerAnimation.Completed += CenterAnimationCompleted;
|
2017-11-10 17:26:15 +01:00
|
|
|
|
|
2012-11-22 21:42:29 +01:00
|
|
|
|
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
|
|
|
|
|
2017-11-10 17:26:15 +01:00
|
|
|
|
this.BeginAnimation(CenterPointProperty, null);
|
2012-10-12 19:22:49 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-03-11 21:03:44 +01:00
|
|
|
|
private void CenterPointPropertyChanged(Location center)
|
2012-10-12 19:22:49 +02:00
|
|
|
|
{
|
2017-11-10 17:26:15 +01:00
|
|
|
|
if (centerAnimation != null)
|
2012-11-22 21:42:29 +01:00
|
|
|
|
{
|
2018-03-11 21:03:44 +01:00
|
|
|
|
SetValueInternal(CenterProperty, center);
|
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);
|
2021-01-07 22:52:41 +01:00
|
|
|
|
|
2017-11-10 17:26:15 +01:00
|
|
|
|
SetValueInternal(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
|
|
|
|
{
|
2021-01-07 22:52:41 +01:00
|
|
|
|
if (maxZoomLevel < MinZoomLevel)
|
2012-10-12 19:22:49 +02:00
|
|
|
|
{
|
2021-01-07 22:52:41 +01:00
|
|
|
|
maxZoomLevel = MinZoomLevel;
|
|
|
|
|
|
|
2017-11-10 17:26:15 +01:00
|
|
|
|
SetValueInternal(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);
|
2021-01-07 22:52:41 +01:00
|
|
|
|
|
2017-11-10 17:26:15 +01:00
|
|
|
|
SetValueInternal(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)
|
|
|
|
|
|
{
|
2017-11-10 17:26:15 +01:00
|
|
|
|
SetValueInternal(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,
|
2017-11-10 17:26:15 +01:00
|
|
|
|
EasingFunction = AnimationEasingFunction
|
2013-11-17 16:52:03 +01:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
zoomLevelAnimation.Completed += ZoomLevelAnimationCompleted;
|
2017-11-10 17:26:15 +01:00
|
|
|
|
|
2013-11-17 16:52:03 +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)
|
|
|
|
|
|
{
|
2017-11-10 17:26:15 +01:00
|
|
|
|
SetValueInternal(ZoomLevelProperty, TargetZoomLevel);
|
|
|
|
|
|
UpdateTransform(true);
|
|
|
|
|
|
|
2012-11-22 21:42:29 +01:00
|
|
|
|
zoomLevelAnimation.Completed -= ZoomLevelAnimationCompleted;
|
|
|
|
|
|
zoomLevelAnimation = null;
|
|
|
|
|
|
|
2017-11-10 17:26:15 +01:00
|
|
|
|
this.BeginAnimation(ZoomLevelProperty, null);
|
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;
|
2021-01-07 22:52:41 +01:00
|
|
|
|
|
2017-11-10 17:26:15 +01:00
|
|
|
|
SetValueInternal(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)
|
|
|
|
|
|
{
|
2017-11-10 17:26:15 +01:00
|
|
|
|
SetValueInternal(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,
|
2017-11-10 17:26:15 +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;
|
2017-11-10 17:26:15 +01:00
|
|
|
|
|
2012-11-22 21:42:29 +01:00
|
|
|
|
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)
|
|
|
|
|
|
{
|
2017-11-10 17:26:15 +01:00
|
|
|
|
SetValueInternal(HeadingProperty, TargetHeading);
|
|
|
|
|
|
UpdateTransform();
|
|
|
|
|
|
|
2012-11-22 21:42:29 +01:00
|
|
|
|
headingAnimation.Completed -= HeadingAnimationCompleted;
|
|
|
|
|
|
headingAnimation = null;
|
2012-10-12 19:22:49 +02:00
|
|
|
|
|
2017-11-10 17:26:15 +01:00
|
|
|
|
this.BeginAnimation(HeadingProperty, null);
|
2012-11-22 21:42:29 +01:00
|
|
|
|
}
|
2012-10-12 19:22:49 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2017-11-10 17:26:15 +01:00
|
|
|
|
private void SetValueInternal(DependencyProperty property, object value)
|
2012-10-12 19:22:49 +02:00
|
|
|
|
{
|
2017-06-25 23:05:48 +02:00
|
|
|
|
internalPropertyChange = true;
|
2021-01-07 22:52:41 +01:00
|
|
|
|
|
2017-06-25 23:05:48 +02:00
|
|
|
|
SetValue(property, value);
|
2021-01-07 22:52:41 +01:00
|
|
|
|
|
2017-06-25 23:05:48 +02:00
|
|
|
|
internalPropertyChange = false;
|
|
|
|
|
|
}
|
2012-10-12 19:22:49 +02:00
|
|
|
|
|
2017-06-25 23:05:48 +02:00
|
|
|
|
private void UpdateTransform(bool resetTransformCenter = false, bool projectionChanged = false)
|
|
|
|
|
|
{
|
2022-11-24 23:24:51 +01:00
|
|
|
|
var transformCenterChanged = false;
|
2020-04-01 18:04:39 +02:00
|
|
|
|
var viewScale = ViewTransform.ZoomLevelToScale(ZoomLevel);
|
2017-06-25 23:05:48 +02:00
|
|
|
|
var projection = MapProjection;
|
|
|
|
|
|
|
2022-03-07 17:28:08 +01:00
|
|
|
|
projection.Center = ProjectionCenter ?? Center;
|
2020-03-26 19:08:20 +01:00
|
|
|
|
|
2022-03-04 22:28:18 +01:00
|
|
|
|
var mapCenter = projection.LocationToMap(transformCenter ?? Center);
|
2014-10-29 18:22:50 +01:00
|
|
|
|
|
2022-03-04 22:28:18 +01:00
|
|
|
|
if (MapProjection.IsValid(mapCenter))
|
2015-11-11 19:48:50 +01:00
|
|
|
|
{
|
2022-11-07 21:10:10 +01:00
|
|
|
|
ViewTransform.SetTransform(mapCenter, viewCenter, viewScale, -Heading);
|
2013-05-13 23:49:48 +02:00
|
|
|
|
|
2022-03-04 22:28:18 +01:00
|
|
|
|
if (transformCenter != null)
|
2013-11-22 18:46:45 +01:00
|
|
|
|
{
|
2022-03-04 22:28:18 +01:00
|
|
|
|
var center = ViewToLocation(new Point(RenderSize.Width / 2d, RenderSize.Height / 2d));
|
2013-04-04 18:01:13 +02:00
|
|
|
|
|
2022-03-04 22:28:18 +01:00
|
|
|
|
if (center != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
center.Longitude = Location.NormalizeLongitude(center.Longitude);
|
2013-04-04 18:01:13 +02:00
|
|
|
|
|
2022-03-05 18:40:57 +01:00
|
|
|
|
if (center.Latitude < -maxLatitude || center.Latitude > maxLatitude)
|
2022-03-04 22:28:18 +01:00
|
|
|
|
{
|
2022-03-05 18:40:57 +01:00
|
|
|
|
center.Latitude = Math.Min(Math.Max(center.Latitude, -maxLatitude), maxLatitude);
|
2022-03-04 22:28:18 +01:00
|
|
|
|
resetTransformCenter = true;
|
|
|
|
|
|
}
|
2013-11-21 21:16:29 +01:00
|
|
|
|
|
2022-03-04 22:28:18 +01:00
|
|
|
|
SetValueInternal(CenterProperty, center);
|
2020-03-26 19:08:20 +01:00
|
|
|
|
|
2022-03-04 22:28:18 +01:00
|
|
|
|
if (centerAnimation == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
SetValueInternal(TargetCenterProperty, center);
|
|
|
|
|
|
}
|
2020-03-26 19:08:20 +01:00
|
|
|
|
|
2022-03-04 22:28:18 +01:00
|
|
|
|
if (resetTransformCenter)
|
|
|
|
|
|
{
|
2022-11-24 23:24:51 +01:00
|
|
|
|
// check if transform center moved across the dateline
|
|
|
|
|
|
transformCenterChanged = Math.Abs(center.Longitude - transformCenter.Longitude) > 180d;
|
|
|
|
|
|
|
2022-03-04 22:28:18 +01:00
|
|
|
|
ResetTransformCenter();
|
|
|
|
|
|
|
2022-03-07 17:28:08 +01:00
|
|
|
|
projection.Center = ProjectionCenter ?? Center;
|
2022-03-06 17:28:27 +01:00
|
|
|
|
|
2022-03-04 22:28:18 +01:00
|
|
|
|
mapCenter = projection.LocationToMap(center);
|
|
|
|
|
|
|
|
|
|
|
|
if (MapProjection.IsValid(mapCenter))
|
|
|
|
|
|
{
|
2022-11-07 21:10:10 +01:00
|
|
|
|
ViewTransform.SetTransform(mapCenter, viewCenter, viewScale, -Heading);
|
2022-03-04 22:28:18 +01:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2013-04-04 18:01:13 +02:00
|
|
|
|
}
|
2012-10-12 19:22:49 +02:00
|
|
|
|
|
2022-11-02 19:49:18 +01:00
|
|
|
|
SetViewScale(ViewTransform.Scale);
|
|
|
|
|
|
|
2022-11-24 23:24:51 +01:00
|
|
|
|
// check if view center moved across the dateline
|
|
|
|
|
|
transformCenterChanged = transformCenterChanged || Math.Abs(Center.Longitude - centerLongitude) > 180d;
|
2022-03-04 22:28:18 +01:00
|
|
|
|
centerLongitude = Center.Longitude;
|
2022-11-24 23:24:51 +01:00
|
|
|
|
|
|
|
|
|
|
OnViewportChanged(new ViewportChangedEventArgs(projectionChanged, transformCenterChanged));
|
2022-03-04 22:28:18 +01:00
|
|
|
|
}
|
2012-10-12 19:22:49 +02:00
|
|
|
|
}
|
2013-05-13 23:49:48 +02:00
|
|
|
|
|
2017-02-05 18:35:30 +01:00
|
|
|
|
protected override void OnViewportChanged(ViewportChangedEventArgs e)
|
2014-11-19 21:11:14 +01:00
|
|
|
|
{
|
2017-02-05 18:35:30 +01:00
|
|
|
|
base.OnViewportChanged(e);
|
2014-11-19 21:11:14 +01:00
|
|
|
|
|
2017-02-05 18:35:30 +01:00
|
|
|
|
ViewportChanged?.Invoke(this, e);
|
2013-05-13 23:49:48 +02:00
|
|
|
|
}
|
2012-10-12 19:22:49 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|