From 27cdfe77ae02eda0ace34c97d4091d693104b84b Mon Sep 17 00:00:00 2001 From: ClemensF Date: Fri, 12 Oct 2012 19:22:49 +0200 Subject: [PATCH] Moved basic map functionality to class MapBase, input event handling to class Map. Fixed method names in class MapItemsControl. --- MapControl/Map.cs | 781 ++-------------------------------- MapControl/MapBase.cs | 780 +++++++++++++++++++++++++++++++++ MapControl/MapControl.csproj | 2 +- MapControl/MapElement.cs | 11 +- MapControl/MapGraticule.cs | 2 +- MapControl/MapItem.cs | 2 +- MapControl/MapItemsControl.cs | 18 +- MapControl/MapPanel.cs | 27 +- MapControl/MapPolyline.cs | 2 +- MapControl/Pushpin.cs | 2 +- 10 files changed, 862 insertions(+), 765 deletions(-) create mode 100644 MapControl/MapBase.cs diff --git a/MapControl/Map.cs b/MapControl/Map.cs index 03c7f87f..17233df3 100644 --- a/MapControl/Map.cs +++ b/MapControl/Map.cs @@ -3,775 +3,86 @@ // Licensed under the Microsoft Public License (Ms-PL) using System; -using System.Collections.Specialized; -using System.Linq; using System.Windows; -using System.Windows.Controls; -using System.Windows.Media; -using System.Windows.Media.Animation; +using System.Windows.Input; namespace MapControl { /// - /// The main map control. Draws map content provided by the TileLayers or the BaseTileLayer property. - /// 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. - /// Map is a MapPanel and hence can contain map overlays like other MapPanels or MapItemsControls. + /// MapBase with input event handling. /// - public partial class Map : MapPanel + public class Map : MapBase { - public const double MeterPerDegree = 1852d * 60d; + private double mouseWheelZoom = 1d; + private Point? mousePosition; - public static readonly DependencyProperty FontSizeProperty = Control.FontSizeProperty.AddOwner(typeof(Map)); - public static readonly DependencyProperty FontFamilyProperty = Control.FontFamilyProperty.AddOwner(typeof(Map)); - public static readonly DependencyProperty FontStyleProperty = Control.FontStyleProperty.AddOwner(typeof(Map)); - public static readonly DependencyProperty FontWeightProperty = Control.FontWeightProperty.AddOwner(typeof(Map)); - public static readonly DependencyProperty FontStretchProperty = Control.FontStretchProperty.AddOwner(typeof(Map)); - public static readonly DependencyProperty ForegroundProperty = Control.ForegroundProperty.AddOwner(typeof(Map)); - - public static readonly DependencyProperty LightForegroundProperty = DependencyProperty.Register( - "LightForeground", typeof(Brush), typeof(Map)); - - public static readonly DependencyProperty DarkForegroundProperty = DependencyProperty.Register( - "DarkForeground", typeof(Brush), typeof(Map)); - - public static readonly DependencyProperty LightBackgroundProperty = DependencyProperty.Register( - "LightBackground", typeof(Brush), typeof(Map)); - - public static readonly DependencyProperty DarkBackgroundProperty = DependencyProperty.Register( - "DarkBackground", typeof(Brush), typeof(Map)); - - public static readonly DependencyProperty TileLayersProperty = DependencyProperty.Register( - "TileLayers", typeof(TileLayerCollection), typeof(Map), new FrameworkPropertyMetadata( - (o, e) => ((Map)o).TileLayersPropertyChanged((TileLayerCollection)e.OldValue, (TileLayerCollection)e.NewValue), - (o, v) => ((Map)o).CoerceTileLayersProperty((TileLayerCollection)v))); - - public static readonly DependencyProperty BaseTileLayerProperty = DependencyProperty.Register( - "BaseTileLayer", typeof(TileLayer), typeof(Map), new FrameworkPropertyMetadata( - (o, e) => ((Map)o).BaseTileLayerPropertyChanged((TileLayer)e.NewValue), - (o, v) => ((Map)o).CoerceBaseTileLayerProperty((TileLayer)v))); - - public static readonly DependencyProperty TileOpacityProperty = DependencyProperty.Register( - "TileOpacity", typeof(double), typeof(Map), new FrameworkPropertyMetadata(1d, - (o, e) => ((Map)o).tileContainer.Opacity = (double)e.NewValue)); - - public static readonly DependencyProperty CenterProperty = DependencyProperty.Register( - "Center", typeof(Location), typeof(Map), new FrameworkPropertyMetadata(new Location(), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, - (o, e) => ((Map)o).CenterPropertyChanged((Location)e.NewValue), - (o, v) => ((Map)o).CoerceCenterProperty((Location)v))); - - public static readonly DependencyProperty TargetCenterProperty = DependencyProperty.Register( - "TargetCenter", typeof(Location), typeof(Map), new FrameworkPropertyMetadata(new Location(), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, - (o, e) => ((Map)o).TargetCenterPropertyChanged((Location)e.NewValue), - (o, v) => ((Map)o).CoerceCenterProperty((Location)v))); - - public static readonly DependencyProperty ZoomLevelProperty = DependencyProperty.Register( - "ZoomLevel", typeof(double), typeof(Map), new FrameworkPropertyMetadata(1d, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, - (o, e) => ((Map)o).ZoomLevelPropertyChanged((double)e.NewValue), - (o, v) => ((Map)o).CoerceZoomLevelProperty((double)v))); - - public static readonly DependencyProperty TargetZoomLevelProperty = DependencyProperty.Register( - "TargetZoomLevel", typeof(double), typeof(Map), new FrameworkPropertyMetadata(1d, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, - (o, e) => ((Map)o).TargetZoomLevelPropertyChanged((double)e.NewValue), - (o, v) => ((Map)o).CoerceZoomLevelProperty((double)v))); - - public static readonly DependencyProperty HeadingProperty = DependencyProperty.Register( - "Heading", typeof(double), typeof(Map), new FrameworkPropertyMetadata(0d, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, - (o, e) => ((Map)o).HeadingPropertyChanged((double)e.NewValue), - (o, v) => ((Map)o).CoerceHeadingProperty((double)v))); - - public static readonly DependencyProperty TargetHeadingProperty = DependencyProperty.Register( - "TargetHeading", typeof(double), typeof(Map), new FrameworkPropertyMetadata(0d, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, - (o, e) => ((Map)o).TargetHeadingPropertyChanged((double)e.NewValue), - (o, v) => ((Map)o).CoerceHeadingProperty((double)v))); - - private static readonly DependencyPropertyKey CenterScalePropertyKey = DependencyProperty.RegisterReadOnly( - "CenterScale", typeof(double), typeof(Map), null); - - public static readonly DependencyProperty CenterScaleProperty = CenterScalePropertyKey.DependencyProperty; - - private readonly TileContainer tileContainer = new TileContainer(); - private readonly MapTransform mapTransform = new MercatorTransform(); - private readonly ScaleTransform scaleTransform = new ScaleTransform(); - private readonly RotateTransform rotateTransform = new RotateTransform(); - private readonly MatrixTransform scaleRotateTransform = new MatrixTransform(); - private Location transformOrigin; - private Point viewportOrigin; - private LocationAnimation centerAnimation; - private DoubleAnimation zoomLevelAnimation; - private DoubleAnimation headingAnimation; - private bool updateTransform = true; - - public Map() + public double MouseWheelZoom { - MinZoomLevel = 1; - MaxZoomLevel = 20; - AddVisualChild(tileContainer); - TileLayers = new TileLayerCollection(); - SetValue(ParentMapPropertyKey, this); + get { return mouseWheelZoom; } + set { mouseWheelZoom = value; } + } - Loaded += (o, e) => + protected override void OnMouseWheel(MouseWheelEventArgs e) + { + base.OnMouseWheel(e); + + ZoomMap(e.GetPosition(this), TargetZoomLevel + mouseWheelZoom * Math.Sign(e.Delta)); + } + + protected override void OnMouseRightButtonDown(MouseButtonEventArgs e) + { + base.OnMouseRightButtonDown(e); + + if (e.ClickCount == 2) { - if (BaseTileLayer == null) - { - BaseTileLayer = new TileLayer - { - Name = "OpenStreetMap", - Description = "© {y} OpenStreetMap Contributors, CC-BY-SA", - TileSource = new TileSource("http://{c}.tile.openstreetmap.org/{z}/{x}/{y}.png") - }; - } - }; - } - - /// - /// Raised when the current viewport has changed. - /// - public event Action ViewportChanged; - - public double MinZoomLevel { get; set; } - public double MaxZoomLevel { get; set; } - - public double FontSize - { - get { return (double)GetValue(FontSizeProperty); } - set { SetValue(FontSizeProperty, value); } - } - - public FontFamily FontFamily - { - get { return (FontFamily)GetValue(FontFamilyProperty); } - set { SetValue(FontFamilyProperty, value); } - } - - public FontStyle FontStyle - { - get { return (FontStyle)GetValue(FontStyleProperty); } - set { SetValue(FontStyleProperty, value); } - } - - public FontWeight FontWeight - { - get { return (FontWeight)GetValue(FontWeightProperty); } - set { SetValue(FontWeightProperty, value); } - } - - public FontStretch FontStretch - { - get { return (FontStretch)GetValue(FontStretchProperty); } - set { SetValue(FontStretchProperty, value); } - } - - public Brush Foreground - { - get { return (Brush)GetValue(ForegroundProperty); } - set { SetValue(ForegroundProperty, value); } - } - - public Brush LightForeground - { - get { return (Brush)GetValue(LightForegroundProperty); } - set { SetValue(LightForegroundProperty, value); } - } - - public Brush DarkForeground - { - get { return (Brush)GetValue(DarkForegroundProperty); } - set { SetValue(DarkForegroundProperty, value); } - } - - public Brush LightBackground - { - get { return (Brush)GetValue(LightBackgroundProperty); } - set { SetValue(LightBackgroundProperty, value); } - } - - public Brush DarkBackground - { - get { return (Brush)GetValue(DarkBackgroundProperty); } - set { SetValue(DarkBackgroundProperty, value); } - } - - /// - /// Gets or sets the TileLayers used by this Map. - /// - public TileLayerCollection TileLayers - { - get { return (TileLayerCollection)GetValue(TileLayersProperty); } - set { SetValue(TileLayersProperty, value); } - } - - /// - /// Gets or sets the base TileLayer used by this Map, i.e. TileLayers[0]. - /// - public TileLayer BaseTileLayer - { - get { return (TileLayer)GetValue(BaseTileLayerProperty); } - set { SetValue(BaseTileLayerProperty, value); } - } - - /// - /// Gets or sets the opacity of the tile layers. - /// - public double TileOpacity - { - get { return (double)GetValue(TileOpacityProperty); } - set { SetValue(TileOpacityProperty, value); } - } - - /// - /// Gets or sets the location of the center point of the Map. - /// - public Location Center - { - get { return (Location)GetValue(CenterProperty); } - set { SetValue(CenterProperty, value); } - } - - /// - /// Gets or sets the target value of a Center animation. - /// - public Location TargetCenter - { - get { return (Location)GetValue(TargetCenterProperty); } - set { SetValue(TargetCenterProperty, value); } - } - - /// - /// Gets or sets the map zoom level. - /// - public double ZoomLevel - { - get { return (double)GetValue(ZoomLevelProperty); } - set { SetValue(ZoomLevelProperty, value); } - } - - /// - /// Gets or sets the target value of a ZoomLevel animation. - /// - public double TargetZoomLevel - { - get { return (double)GetValue(TargetZoomLevelProperty); } - set { SetValue(TargetZoomLevelProperty, value); } - } - - /// - /// Gets or sets the map heading, or clockwise rotation angle in degrees. - /// - public double Heading - { - get { return (double)GetValue(HeadingProperty); } - set { SetValue(HeadingProperty, value); } - } - - /// - /// Gets or sets the target value of a Heading animation. - /// - public double TargetHeading - { - get { return (double)GetValue(TargetHeadingProperty); } - set { SetValue(TargetHeadingProperty, value); } - } - - /// - /// Gets the map scale at the Center location as viewport coordinate units (pixels) per meter. - /// - public double CenterScale - { - get { return (double)GetValue(CenterScaleProperty); } - private set { SetValue(CenterScalePropertyKey, value); } - } - - /// - /// Gets the transformation from geographic coordinates to cartesian map coordinates. - /// - public MapTransform MapTransform - { - get { return mapTransform; } - } - - /// - /// Gets the transformation from cartesian map coordinates to viewport coordinates. - /// - public Transform ViewportTransform - { - get { return tileContainer.ViewportTransform; } - } - - /// - /// Gets the scaling transformation from meters to viewport coordinate units (pixels) - /// at the viewport center point. - /// - public Transform ScaleTransform - { - get { return scaleTransform; } - } - - /// - /// Gets the transformation that rotates by the value of the Heading property. - /// - public Transform RotateTransform - { - get { return rotateTransform; } - } - - /// - /// Gets the combination of ScaleTransform and RotateTransform - /// - public Transform ScaleRotateTransform - { - get { return scaleRotateTransform; } - } - - /// - /// Transforms a geographic location to a viewport coordinates point. - /// - public Point LocationToViewportPoint(Location location) - { - return ViewportTransform.Transform(MapTransform.Transform(location)); - } - - /// - /// Transforms a viewport coordinates point to a geographic location. - /// - public Location ViewportPointToLocation(Point point) - { - return MapTransform.TransformBack(ViewportTransform.Inverse.Transform(point)); - } - - /// - /// Sets a temporary origin location in geographic coordinates for scaling and rotation transformations. - /// This origin location is automatically removed when the Center property is set by application code. - /// - public void SetTransformOrigin(Location origin) - { - transformOrigin = origin; - viewportOrigin = LocationToViewportPoint(origin); - } - - /// - /// Sets a temporary origin point in viewport coordinates for scaling and rotation transformations. - /// This origin point is automatically removed when the Center property is set by application code. - /// - public void SetTransformOrigin(Point origin) - { - viewportOrigin.X = Math.Min(Math.Max(origin.X, 0d), RenderSize.Width); - viewportOrigin.Y = Math.Min(Math.Max(origin.Y, 0d), RenderSize.Height); - transformOrigin = CoerceCenterProperty(ViewportPointToLocation(viewportOrigin)); - } - - /// - /// Removes the temporary transform origin point set by SetTransformOrigin. - /// - public void ResetTransformOrigin() - { - transformOrigin = null; - viewportOrigin = new Point(RenderSize.Width / 2d, RenderSize.Height / 2d); - } - - /// - /// Changes the Center property according to the specified translation in viewport coordinates. - /// - public void TranslateMap(Vector translation) - { - if (translation.X != 0d || translation.Y != 0d) - { - if (transformOrigin != null) - { - viewportOrigin += translation; - UpdateTransform(); - } - else - { - Center = ViewportPointToLocation(viewportOrigin - translation); - } + ZoomMap(e.GetPosition(this), Math.Ceiling(ZoomLevel - 1.5)); } } - /// - /// 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. - /// - public void TransformMap(Point origin, Vector translation, double rotation, double scale) + protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e) { - if (rotation != 0d || scale != 1d) + base.OnMouseLeftButtonDown(e); + + if (e.ClickCount == 1) { - SetTransformOrigin(origin); - updateTransform = false; - Heading = (((Heading + rotation) % 360d) + 360d) % 360d; - ZoomLevel += Math.Log(scale, 2d); - updateTransform = true; - UpdateTransform(); + mousePosition = e.GetPosition(this); + CaptureMouse(); } - - ResetTransformOrigin(); - TranslateMap(translation); - } - - /// - /// Sets the value of the ZoomLevel property while retaining the specified origin point - /// in viewport coordinates. - /// - public void ZoomMap(Point origin, double zoomLevel) - { - double targetZoomLebel = TargetZoomLevel; - TargetZoomLevel = zoomLevel; - - if (TargetZoomLevel != targetZoomLebel) // TargetZoomLevel might be coerced + else if (e.ClickCount == 2) { - SetTransformOrigin(origin); + ZoomMap(e.GetPosition(this), Math.Floor(ZoomLevel + 1.5)); } } - /// - /// Gets the map scale at the specified location as viewport coordinate units (pixels) per meter. - /// - public double GetMapScale(Location location) + protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e) { - return mapTransform.RelativeScale(location) * Math.Pow(2d, ZoomLevel) * 256d / (MeterPerDegree * 360d); - } + base.OnMouseLeftButtonUp(e); - protected override int VisualChildrenCount - { - get { return InternalChildren.Count + 1; } - } - - protected override Visual GetVisualChild(int index) - { - if (index == 0) + if (mousePosition.HasValue) { - return tileContainer; - } - - return InternalChildren[index - 1]; - } - - protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo) - { - base.OnRenderSizeChanged(sizeInfo); - - ResetTransformOrigin(); - UpdateTransform(); - } - - protected override void OnRender(DrawingContext drawingContext) - { - drawingContext.DrawRectangle(Background, null, new Rect(RenderSize)); - } - - protected override void OnViewportChanged() - { - base.OnViewportChanged(); - - if (ViewportChanged != null) - { - ViewportChanged(); + mousePosition = null; + ReleaseMouseCapture(); } } - private void TileLayerCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) + protected override void OnMouseMove(MouseEventArgs e) { - switch (e.Action) + base.OnMouseMove(e); + + if (mousePosition.HasValue) { - case NotifyCollectionChangedAction.Add: - tileContainer.AddTileLayers(e.NewStartingIndex, e.NewItems.Cast()); - break; - - case NotifyCollectionChangedAction.Remove: - tileContainer.RemoveTileLayers(e.OldStartingIndex, e.OldItems.Cast()); - break; - - case NotifyCollectionChangedAction.Move: - case NotifyCollectionChangedAction.Replace: - tileContainer.RemoveTileLayers(e.OldStartingIndex, e.OldItems.Cast()); - tileContainer.AddTileLayers(e.NewStartingIndex, e.NewItems.Cast()); - break; - - case NotifyCollectionChangedAction.Reset: - tileContainer.ClearTileLayers(); - if (e.NewItems != null) - { - tileContainer.AddTileLayers(0, e.NewItems.Cast()); - } - break; - } - - UpdateBaseTileLayer(); - } - - private void TileLayersPropertyChanged(TileLayerCollection oldTileLayers, TileLayerCollection newTileLayers) - { - tileContainer.ClearTileLayers(); - - if (oldTileLayers != null) - { - oldTileLayers.CollectionChanged -= TileLayerCollectionChanged; - } - - if (newTileLayers != null) - { - newTileLayers.CollectionChanged += TileLayerCollectionChanged; - tileContainer.AddTileLayers(0, newTileLayers); - } - - UpdateBaseTileLayer(); - } - - private TileLayerCollection CoerceTileLayersProperty(TileLayerCollection tileLayers) - { - if (tileLayers == null) - { - tileLayers = new TileLayerCollection(); - } - - return tileLayers; - } - - private void BaseTileLayerPropertyChanged(TileLayer baseTileLayer) - { - if (baseTileLayer != null) - { - if (TileLayers.Count == 0) - { - TileLayers.Add(baseTileLayer); - } - else if (TileLayers[0] != baseTileLayer) - { - TileLayers[0] = baseTileLayer; - } - } - - if (baseTileLayer != null && baseTileLayer.HasDarkBackground) - { - if (DarkForeground != null) - { - Foreground = DarkForeground; - } - - if (DarkBackground != null) - { - Background = DarkBackground; - } - } - else - { - if (LightForeground != null) - { - Foreground = LightForeground; - } - - if (LightBackground != null) - { - Background = LightBackground; - } + Point position = e.GetPosition(this); + TranslateMap(position - mousePosition.Value); + mousePosition = position; } } - private TileLayer CoerceBaseTileLayerProperty(TileLayer baseTileLayer) + protected override void OnManipulationDelta(ManipulationDeltaEventArgs e) { - if (baseTileLayer == null && TileLayers.Count > 0) - { - baseTileLayer = TileLayers[0]; - } + base.OnManipulationDelta(e); - return baseTileLayer; - } - - private void UpdateBaseTileLayer() - { - TileLayer baseTileLayer = TileLayers.FirstOrDefault(); - - if (BaseTileLayer != baseTileLayer) - { - BaseTileLayer = baseTileLayer; - } - } - - private void CenterPropertyChanged(Location center) - { - if (updateTransform) - { - ResetTransformOrigin(); - UpdateTransform(); - } - - if (centerAnimation == null) - { - TargetCenter = center; - } - } - - private void TargetCenterPropertyChanged(Location targetCenter) - { - if (targetCenter != Center) - { - if (centerAnimation != null) - { - centerAnimation.Completed -= CenterAnimationCompleted; - } - - centerAnimation = new LocationAnimation - { - From = Center, - To = targetCenter, - Duration = TimeSpan.FromSeconds(0.5), - FillBehavior = FillBehavior.Stop, - EasingFunction = new QuadraticEase { EasingMode = EasingMode.EaseOut } - }; - - centerAnimation.Completed += CenterAnimationCompleted; - BeginAnimation(CenterProperty, centerAnimation); - } - } - - private void CenterAnimationCompleted(object sender, EventArgs e) - { - Center = TargetCenter; - centerAnimation.Completed -= CenterAnimationCompleted; - centerAnimation = null; - } - - private Location CoerceCenterProperty(Location location) - { - location.Latitude = Math.Min(Math.Max(location.Latitude, -MapTransform.MaxLatitude), MapTransform.MaxLatitude); - location.Longitude = Location.NormalizeLongitude(location.Longitude); - return location; - } - - private void ZoomLevelPropertyChanged(double zoomLevel) - { - if (updateTransform) - { - UpdateTransform(); - } - - if (zoomLevelAnimation == null) - { - TargetZoomLevel = zoomLevel; - } - } - - private void TargetZoomLevelPropertyChanged(double targetZoomLevel) - { - if (targetZoomLevel != ZoomLevel) - { - if (zoomLevelAnimation != null) - { - zoomLevelAnimation.Completed -= ZoomLevelAnimationCompleted; - } - - zoomLevelAnimation = new DoubleAnimation - { - From = ZoomLevel, - To = targetZoomLevel, - Duration = TimeSpan.FromSeconds(0.5), - FillBehavior = FillBehavior.Stop, - EasingFunction = new QuadraticEase { EasingMode = EasingMode.EaseOut } - }; - - zoomLevelAnimation.Completed += ZoomLevelAnimationCompleted; - BeginAnimation(ZoomLevelProperty, zoomLevelAnimation); - } - } - - private void ZoomLevelAnimationCompleted(object sender, EventArgs e) - { - ZoomLevel = TargetZoomLevel; - zoomLevelAnimation.Completed -= ZoomLevelAnimationCompleted; - zoomLevelAnimation = null; - ResetTransformOrigin(); - } - - private double CoerceZoomLevelProperty(double zoomLevel) - { - return Math.Min(Math.Max(zoomLevel, MinZoomLevel), MaxZoomLevel); - } - - private void HeadingPropertyChanged(double heading) - { - if (updateTransform) - { - UpdateTransform(); - } - - if (headingAnimation == null) - { - TargetHeading = heading; - } - } - - private void TargetHeadingPropertyChanged(double targetHeading) - { - if (targetHeading != Heading) - { - if (headingAnimation != null) - { - headingAnimation.Completed -= HeadingAnimationCompleted; - } - - double delta = targetHeading - Heading; - - if (delta > 180d) - { - delta -= 360d; - } - else if (delta < -180d) - { - delta += 360d; - } - - headingAnimation = new DoubleAnimation - { - From = Heading, - By = delta, - Duration = TimeSpan.FromSeconds(0.5), - FillBehavior = FillBehavior.Stop, - EasingFunction = new QuadraticEase { EasingMode = EasingMode.EaseOut } - }; - - headingAnimation.Completed += HeadingAnimationCompleted; - BeginAnimation(HeadingProperty, headingAnimation); - } - } - - private void HeadingAnimationCompleted(object sender, EventArgs e) - { - Heading = TargetHeading; - headingAnimation.Completed -= HeadingAnimationCompleted; - headingAnimation = null; - } - - private double CoerceHeadingProperty(double heading) - { - return ((heading % 360d) + 360d) % 360d; - } - - private void UpdateTransform() - { - double scale; - - if (transformOrigin != null) - { - scale = tileContainer.SetViewportTransform(ZoomLevel, Heading, MapTransform.Transform(transformOrigin), viewportOrigin, RenderSize); - updateTransform = false; - Center = ViewportPointToLocation(new Point(RenderSize.Width / 2d, RenderSize.Height / 2d)); - updateTransform = true; - } - else - { - scale = tileContainer.SetViewportTransform(ZoomLevel, Heading, MapTransform.Transform(Center), viewportOrigin, RenderSize); - } - - scale *= MapTransform.RelativeScale(Center) / MeterPerDegree; // Pixels per meter at center latitude - - CenterScale = scale; - scaleTransform.ScaleX = scale; - scaleTransform.ScaleY = scale; - rotateTransform.Angle = Heading; - scaleRotateTransform.Matrix = scaleTransform.Value * rotateTransform.Value; - - OnViewportChanged(); + ManipulationDelta d = e.DeltaManipulation; + TransformMap(e.ManipulationOrigin, d.Translation, d.Rotation, (d.Scale.X + d.Scale.Y) / 2d); } } } diff --git a/MapControl/MapBase.cs b/MapControl/MapBase.cs new file mode 100644 index 00000000..f6f95bad --- /dev/null +++ b/MapControl/MapBase.cs @@ -0,0 +1,780 @@ +// WPF MapControl - http://wpfmapcontrol.codeplex.com/ +// Copyright © 2012 Clemens Fischer +// Licensed under the Microsoft Public License (Ms-PL) + +using System; +using System.Collections.Specialized; +using System.Linq; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Media; +using System.Windows.Media.Animation; + +namespace MapControl +{ + /// + /// The map control. Draws map content provided by the TileLayers or the BaseTileLayer property. + /// The visible map area is defined by the Center and ZoomLevel properties. The map can be rotated + /// by an angle that is given by the Heading property. + /// MapBase is a MapPanel and hence can contain map overlays like other MapPanels or MapItemsControls. + /// + public class MapBase : MapPanel + { + public const double MeterPerDegree = 1852d * 60d; + + public static readonly DependencyProperty FontSizeProperty = Control.FontSizeProperty.AddOwner(typeof(MapBase)); + public static readonly DependencyProperty FontFamilyProperty = Control.FontFamilyProperty.AddOwner(typeof(MapBase)); + public static readonly DependencyProperty FontStyleProperty = Control.FontStyleProperty.AddOwner(typeof(MapBase)); + public static readonly DependencyProperty FontWeightProperty = Control.FontWeightProperty.AddOwner(typeof(MapBase)); + public static readonly DependencyProperty FontStretchProperty = Control.FontStretchProperty.AddOwner(typeof(MapBase)); + public static readonly DependencyProperty ForegroundProperty = Control.ForegroundProperty.AddOwner(typeof(MapBase)); + + public static readonly DependencyProperty LightForegroundProperty = DependencyProperty.Register( + "LightForeground", typeof(Brush), typeof(MapBase)); + + public static readonly DependencyProperty DarkForegroundProperty = DependencyProperty.Register( + "DarkForeground", typeof(Brush), typeof(MapBase)); + + public static readonly DependencyProperty LightBackgroundProperty = DependencyProperty.Register( + "LightBackground", typeof(Brush), typeof(MapBase)); + + public static readonly DependencyProperty DarkBackgroundProperty = DependencyProperty.Register( + "DarkBackground", typeof(Brush), typeof(MapBase)); + + public static readonly DependencyProperty TileLayersProperty = DependencyProperty.Register( + "TileLayers", typeof(TileLayerCollection), typeof(MapBase), new FrameworkPropertyMetadata( + (o, e) => ((MapBase)o).TileLayersPropertyChanged((TileLayerCollection)e.OldValue, (TileLayerCollection)e.NewValue), + (o, v) => ((MapBase)o).CoerceTileLayersProperty((TileLayerCollection)v))); + + public static readonly DependencyProperty BaseTileLayerProperty = DependencyProperty.Register( + "BaseTileLayer", typeof(TileLayer), typeof(MapBase), new FrameworkPropertyMetadata( + (o, e) => ((MapBase)o).BaseTileLayerPropertyChanged((TileLayer)e.NewValue), + (o, v) => ((MapBase)o).CoerceBaseTileLayerProperty((TileLayer)v))); + + public static readonly DependencyProperty TileOpacityProperty = DependencyProperty.Register( + "TileOpacity", typeof(double), typeof(MapBase), new FrameworkPropertyMetadata(1d, + (o, e) => ((MapBase)o).tileContainer.Opacity = (double)e.NewValue)); + + public static readonly DependencyProperty CenterProperty = DependencyProperty.Register( + "Center", typeof(Location), typeof(MapBase), new FrameworkPropertyMetadata(new Location(), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, + (o, e) => ((MapBase)o).CenterPropertyChanged((Location)e.NewValue), + (o, v) => ((MapBase)o).CoerceCenterProperty((Location)v))); + + public static readonly DependencyProperty TargetCenterProperty = DependencyProperty.Register( + "TargetCenter", typeof(Location), typeof(MapBase), new FrameworkPropertyMetadata(new Location(), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, + (o, e) => ((MapBase)o).TargetCenterPropertyChanged((Location)e.NewValue), + (o, v) => ((MapBase)o).CoerceCenterProperty((Location)v))); + + public static readonly DependencyProperty ZoomLevelProperty = DependencyProperty.Register( + "ZoomLevel", typeof(double), typeof(MapBase), new FrameworkPropertyMetadata(1d, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, + (o, e) => ((MapBase)o).ZoomLevelPropertyChanged((double)e.NewValue), + (o, v) => ((MapBase)o).CoerceZoomLevelProperty((double)v))); + + public static readonly DependencyProperty TargetZoomLevelProperty = DependencyProperty.Register( + "TargetZoomLevel", typeof(double), typeof(MapBase), new FrameworkPropertyMetadata(1d, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, + (o, e) => ((MapBase)o).TargetZoomLevelPropertyChanged((double)e.NewValue), + (o, v) => ((MapBase)o).CoerceZoomLevelProperty((double)v))); + + public static readonly DependencyProperty HeadingProperty = DependencyProperty.Register( + "Heading", typeof(double), typeof(MapBase), new FrameworkPropertyMetadata(0d, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, + (o, e) => ((MapBase)o).HeadingPropertyChanged((double)e.NewValue), + (o, v) => ((MapBase)o).CoerceHeadingProperty((double)v))); + + public static readonly DependencyProperty TargetHeadingProperty = DependencyProperty.Register( + "TargetHeading", typeof(double), typeof(MapBase), new FrameworkPropertyMetadata(0d, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, + (o, e) => ((MapBase)o).TargetHeadingPropertyChanged((double)e.NewValue), + (o, v) => ((MapBase)o).CoerceHeadingProperty((double)v))); + + private static readonly DependencyPropertyKey CenterScalePropertyKey = DependencyProperty.RegisterReadOnly( + "CenterScale", typeof(double), typeof(MapBase), null); + + public static readonly DependencyProperty CenterScaleProperty = CenterScalePropertyKey.DependencyProperty; + + private readonly TileContainer tileContainer = new TileContainer(); + private readonly MapTransform mapTransform = new MercatorTransform(); + private readonly ScaleTransform scaleTransform = new ScaleTransform(); + private readonly RotateTransform rotateTransform = new RotateTransform(); + private readonly MatrixTransform scaleRotateTransform = new MatrixTransform(); + private Location transformOrigin; + private Point viewportOrigin; + private LocationAnimation centerAnimation; + private DoubleAnimation zoomLevelAnimation; + private DoubleAnimation headingAnimation; + private bool updateTransform = true; + + public MapBase() + { + ClipToBounds = true; + MinZoomLevel = 1; + MaxZoomLevel = 20; + + AddVisualChild(tileContainer); + TileLayers = new TileLayerCollection(); + + SetValue(ParentMapPropertyKey, this); + + Loaded += (o, e) => + { + if (BaseTileLayer == null) + { + BaseTileLayer = new TileLayer + { + Name = "OpenStreetMap", + Description = "© {y} OpenStreetMap Contributors, CC-BY-SA", + TileSource = new TileSource("http://{c}.tile.openstreetmap.org/{z}/{x}/{y}.png") + }; + } + }; + } + + /// + /// Raised when the current viewport has changed. + /// + public event EventHandler ViewportChanged; + + public double MinZoomLevel { get; set; } + public double MaxZoomLevel { get; set; } + + public double FontSize + { + get { return (double)GetValue(FontSizeProperty); } + set { SetValue(FontSizeProperty, value); } + } + + public FontFamily FontFamily + { + get { return (FontFamily)GetValue(FontFamilyProperty); } + set { SetValue(FontFamilyProperty, value); } + } + + public FontStyle FontStyle + { + get { return (FontStyle)GetValue(FontStyleProperty); } + set { SetValue(FontStyleProperty, value); } + } + + public FontWeight FontWeight + { + get { return (FontWeight)GetValue(FontWeightProperty); } + set { SetValue(FontWeightProperty, value); } + } + + public FontStretch FontStretch + { + get { return (FontStretch)GetValue(FontStretchProperty); } + set { SetValue(FontStretchProperty, value); } + } + + public Brush Foreground + { + get { return (Brush)GetValue(ForegroundProperty); } + set { SetValue(ForegroundProperty, value); } + } + + public Brush LightForeground + { + get { return (Brush)GetValue(LightForegroundProperty); } + set { SetValue(LightForegroundProperty, value); } + } + + public Brush DarkForeground + { + get { return (Brush)GetValue(DarkForegroundProperty); } + set { SetValue(DarkForegroundProperty, value); } + } + + public Brush LightBackground + { + get { return (Brush)GetValue(LightBackgroundProperty); } + set { SetValue(LightBackgroundProperty, value); } + } + + public Brush DarkBackground + { + get { return (Brush)GetValue(DarkBackgroundProperty); } + set { SetValue(DarkBackgroundProperty, value); } + } + + /// + /// Gets or sets the TileLayers used by this Map. + /// + public TileLayerCollection TileLayers + { + get { return (TileLayerCollection)GetValue(TileLayersProperty); } + set { SetValue(TileLayersProperty, value); } + } + + /// + /// Gets or sets the base TileLayer used by this Map, i.e. TileLayers[0]. + /// + public TileLayer BaseTileLayer + { + get { return (TileLayer)GetValue(BaseTileLayerProperty); } + set { SetValue(BaseTileLayerProperty, value); } + } + + /// + /// Gets or sets the opacity of the tile layers. + /// + public double TileOpacity + { + get { return (double)GetValue(TileOpacityProperty); } + set { SetValue(TileOpacityProperty, value); } + } + + /// + /// Gets or sets the location of the center point of the Map. + /// + public Location Center + { + get { return (Location)GetValue(CenterProperty); } + set { SetValue(CenterProperty, value); } + } + + /// + /// Gets or sets the target value of a Center animation. + /// + public Location TargetCenter + { + get { return (Location)GetValue(TargetCenterProperty); } + set { SetValue(TargetCenterProperty, value); } + } + + /// + /// Gets or sets the map zoom level. + /// + public double ZoomLevel + { + get { return (double)GetValue(ZoomLevelProperty); } + set { SetValue(ZoomLevelProperty, value); } + } + + /// + /// Gets or sets the target value of a ZoomLevel animation. + /// + public double TargetZoomLevel + { + get { return (double)GetValue(TargetZoomLevelProperty); } + set { SetValue(TargetZoomLevelProperty, value); } + } + + /// + /// Gets or sets the map heading, or clockwise rotation angle in degrees. + /// + public double Heading + { + get { return (double)GetValue(HeadingProperty); } + set { SetValue(HeadingProperty, value); } + } + + /// + /// Gets or sets the target value of a Heading animation. + /// + public double TargetHeading + { + get { return (double)GetValue(TargetHeadingProperty); } + set { SetValue(TargetHeadingProperty, value); } + } + + /// + /// Gets the map scale at the Center location as viewport coordinate units (pixels) per meter. + /// + public double CenterScale + { + get { return (double)GetValue(CenterScaleProperty); } + private set { SetValue(CenterScalePropertyKey, value); } + } + + /// + /// Gets the transformation from geographic coordinates to cartesian map coordinates. + /// + public MapTransform MapTransform + { + get { return mapTransform; } + } + + /// + /// Gets the transformation from cartesian map coordinates to viewport coordinates. + /// + public Transform ViewportTransform + { + get { return tileContainer.ViewportTransform; } + } + + /// + /// Gets the scaling transformation from meters to viewport coordinate units (pixels) + /// at the viewport center point. + /// + public Transform ScaleTransform + { + get { return scaleTransform; } + } + + /// + /// Gets the transformation that rotates by the value of the Heading property. + /// + public Transform RotateTransform + { + get { return rotateTransform; } + } + + /// + /// Gets the combination of ScaleTransform and RotateTransform + /// + public Transform ScaleRotateTransform + { + get { return scaleRotateTransform; } + } + + /// + /// Transforms a geographic location to a viewport coordinates point. + /// + public Point LocationToViewportPoint(Location location) + { + return ViewportTransform.Transform(MapTransform.Transform(location)); + } + + /// + /// Transforms a viewport coordinates point to a geographic location. + /// + public Location ViewportPointToLocation(Point point) + { + return MapTransform.TransformBack(ViewportTransform.Inverse.Transform(point)); + } + + /// + /// Sets a temporary origin location in geographic coordinates for scaling and rotation transformations. + /// This origin location is automatically removed when the Center property is set by application code. + /// + public void SetTransformOrigin(Location origin) + { + transformOrigin = origin; + viewportOrigin = LocationToViewportPoint(origin); + } + + /// + /// Sets a temporary origin point in viewport coordinates for scaling and rotation transformations. + /// This origin point is automatically removed when the Center property is set by application code. + /// + public void SetTransformOrigin(Point origin) + { + viewportOrigin.X = Math.Min(Math.Max(origin.X, 0d), RenderSize.Width); + viewportOrigin.Y = Math.Min(Math.Max(origin.Y, 0d), RenderSize.Height); + transformOrigin = CoerceCenterProperty(ViewportPointToLocation(viewportOrigin)); + } + + /// + /// Removes the temporary transform origin point set by SetTransformOrigin. + /// + public void ResetTransformOrigin() + { + transformOrigin = null; + viewportOrigin = new Point(RenderSize.Width / 2d, RenderSize.Height / 2d); + } + + /// + /// Changes the Center property according to the specified translation in viewport coordinates. + /// + public void TranslateMap(Vector translation) + { + if (translation.X != 0d || translation.Y != 0d) + { + if (transformOrigin != null) + { + viewportOrigin += translation; + UpdateTransform(); + } + else + { + Center = ViewportPointToLocation(viewportOrigin - translation); + } + } + } + + /// + /// 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. + /// + public void TransformMap(Point origin, Vector translation, double rotation, double scale) + { + if (rotation != 0d || scale != 1d) + { + SetTransformOrigin(origin); + updateTransform = false; + Heading = (((Heading + rotation) % 360d) + 360d) % 360d; + ZoomLevel += Math.Log(scale, 2d); + updateTransform = true; + UpdateTransform(); + } + + ResetTransformOrigin(); + TranslateMap(translation); + } + + /// + /// Sets the value of the ZoomLevel property while retaining the specified origin point + /// in viewport coordinates. + /// + public void ZoomMap(Point origin, double zoomLevel) + { + double targetZoomLebel = TargetZoomLevel; + TargetZoomLevel = zoomLevel; + + if (TargetZoomLevel != targetZoomLebel) // TargetZoomLevel might be coerced + { + SetTransformOrigin(origin); + } + } + + /// + /// Gets the map scale at the specified location as viewport coordinate units (pixels) per meter. + /// + public double GetMapScale(Location location) + { + return mapTransform.RelativeScale(location) * Math.Pow(2d, ZoomLevel) * 256d / (MeterPerDegree * 360d); + } + + protected override int VisualChildrenCount + { + get { return InternalChildren.Count + 1; } + } + + protected override Visual GetVisualChild(int index) + { + if (index == 0) + { + return tileContainer; + } + + return InternalChildren[index - 1]; + } + + protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo) + { + base.OnRenderSizeChanged(sizeInfo); + + ResetTransformOrigin(); + UpdateTransform(); + } + + protected override void OnRender(DrawingContext drawingContext) + { + drawingContext.DrawRectangle(Background, null, new Rect(RenderSize)); + } + + protected override void OnViewportChanged() + { + base.OnViewportChanged(); + + if (ViewportChanged != null) + { + ViewportChanged(this, EventArgs.Empty); + } + } + + private void TileLayerCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) + { + switch (e.Action) + { + case NotifyCollectionChangedAction.Add: + tileContainer.AddTileLayers(e.NewStartingIndex, e.NewItems.Cast()); + break; + + case NotifyCollectionChangedAction.Remove: + tileContainer.RemoveTileLayers(e.OldStartingIndex, e.OldItems.Cast()); + break; + + case NotifyCollectionChangedAction.Move: + case NotifyCollectionChangedAction.Replace: + tileContainer.RemoveTileLayers(e.OldStartingIndex, e.OldItems.Cast()); + tileContainer.AddTileLayers(e.NewStartingIndex, e.NewItems.Cast()); + break; + + case NotifyCollectionChangedAction.Reset: + tileContainer.ClearTileLayers(); + if (e.NewItems != null) + { + tileContainer.AddTileLayers(0, e.NewItems.Cast()); + } + break; + } + + UpdateBaseTileLayer(); + } + + private void TileLayersPropertyChanged(TileLayerCollection oldTileLayers, TileLayerCollection newTileLayers) + { + tileContainer.ClearTileLayers(); + + if (oldTileLayers != null) + { + oldTileLayers.CollectionChanged -= TileLayerCollectionChanged; + } + + if (newTileLayers != null) + { + newTileLayers.CollectionChanged += TileLayerCollectionChanged; + tileContainer.AddTileLayers(0, newTileLayers); + } + + UpdateBaseTileLayer(); + } + + private TileLayerCollection CoerceTileLayersProperty(TileLayerCollection tileLayers) + { + if (tileLayers == null) + { + tileLayers = new TileLayerCollection(); + } + + return tileLayers; + } + + private void BaseTileLayerPropertyChanged(TileLayer baseTileLayer) + { + if (baseTileLayer != null) + { + if (TileLayers.Count == 0) + { + TileLayers.Add(baseTileLayer); + } + else if (TileLayers[0] != baseTileLayer) + { + TileLayers[0] = baseTileLayer; + } + } + + if (baseTileLayer != null && baseTileLayer.HasDarkBackground) + { + if (DarkForeground != null) + { + Foreground = DarkForeground; + } + + if (DarkBackground != null) + { + Background = DarkBackground; + } + } + else + { + if (LightForeground != null) + { + Foreground = LightForeground; + } + + if (LightBackground != null) + { + Background = LightBackground; + } + } + } + + private TileLayer CoerceBaseTileLayerProperty(TileLayer baseTileLayer) + { + if (baseTileLayer == null && TileLayers.Count > 0) + { + baseTileLayer = TileLayers[0]; + } + + return baseTileLayer; + } + + private void UpdateBaseTileLayer() + { + TileLayer baseTileLayer = TileLayers.FirstOrDefault(); + + if (BaseTileLayer != baseTileLayer) + { + BaseTileLayer = baseTileLayer; + } + } + + private void CenterPropertyChanged(Location center) + { + if (updateTransform) + { + ResetTransformOrigin(); + UpdateTransform(); + } + + if (centerAnimation == null) + { + TargetCenter = center; + } + } + + private void TargetCenterPropertyChanged(Location targetCenter) + { + if (targetCenter != Center) + { + if (centerAnimation != null) + { + centerAnimation.Completed -= CenterAnimationCompleted; + } + + centerAnimation = new LocationAnimation + { + From = Center, + To = targetCenter, + Duration = TimeSpan.FromSeconds(0.5), + FillBehavior = FillBehavior.Stop, + EasingFunction = new QuadraticEase { EasingMode = EasingMode.EaseOut } + }; + + centerAnimation.Completed += CenterAnimationCompleted; + BeginAnimation(CenterProperty, centerAnimation); + } + } + + private void CenterAnimationCompleted(object sender, EventArgs e) + { + Center = TargetCenter; + centerAnimation.Completed -= CenterAnimationCompleted; + centerAnimation = null; + } + + private Location CoerceCenterProperty(Location location) + { + location.Latitude = Math.Min(Math.Max(location.Latitude, -MapTransform.MaxLatitude), MapTransform.MaxLatitude); + location.Longitude = Location.NormalizeLongitude(location.Longitude); + return location; + } + + private void ZoomLevelPropertyChanged(double zoomLevel) + { + if (updateTransform) + { + UpdateTransform(); + } + + if (zoomLevelAnimation == null) + { + TargetZoomLevel = zoomLevel; + } + } + + private void TargetZoomLevelPropertyChanged(double targetZoomLevel) + { + if (targetZoomLevel != ZoomLevel) + { + if (zoomLevelAnimation != null) + { + zoomLevelAnimation.Completed -= ZoomLevelAnimationCompleted; + } + + zoomLevelAnimation = new DoubleAnimation + { + From = ZoomLevel, + To = targetZoomLevel, + Duration = TimeSpan.FromSeconds(0.5), + FillBehavior = FillBehavior.Stop, + EasingFunction = new QuadraticEase { EasingMode = EasingMode.EaseOut } + }; + + zoomLevelAnimation.Completed += ZoomLevelAnimationCompleted; + BeginAnimation(ZoomLevelProperty, zoomLevelAnimation); + } + } + + private void ZoomLevelAnimationCompleted(object sender, EventArgs e) + { + ZoomLevel = TargetZoomLevel; + zoomLevelAnimation.Completed -= ZoomLevelAnimationCompleted; + zoomLevelAnimation = null; + ResetTransformOrigin(); + } + + private double CoerceZoomLevelProperty(double zoomLevel) + { + return Math.Min(Math.Max(zoomLevel, MinZoomLevel), MaxZoomLevel); + } + + private void HeadingPropertyChanged(double heading) + { + if (updateTransform) + { + UpdateTransform(); + } + + if (headingAnimation == null) + { + TargetHeading = heading; + } + } + + private void TargetHeadingPropertyChanged(double targetHeading) + { + if (targetHeading != Heading) + { + if (headingAnimation != null) + { + headingAnimation.Completed -= HeadingAnimationCompleted; + } + + double delta = targetHeading - Heading; + + if (delta > 180d) + { + delta -= 360d; + } + else if (delta < -180d) + { + delta += 360d; + } + + headingAnimation = new DoubleAnimation + { + From = Heading, + By = delta, + Duration = TimeSpan.FromSeconds(0.5), + FillBehavior = FillBehavior.Stop, + EasingFunction = new QuadraticEase { EasingMode = EasingMode.EaseOut } + }; + + headingAnimation.Completed += HeadingAnimationCompleted; + BeginAnimation(HeadingProperty, headingAnimation); + } + } + + private void HeadingAnimationCompleted(object sender, EventArgs e) + { + Heading = TargetHeading; + headingAnimation.Completed -= HeadingAnimationCompleted; + headingAnimation = null; + } + + private double CoerceHeadingProperty(double heading) + { + return ((heading % 360d) + 360d) % 360d; + } + + private void UpdateTransform() + { + double scale; + + if (transformOrigin != null) + { + scale = tileContainer.SetViewportTransform(ZoomLevel, Heading, MapTransform.Transform(transformOrigin), viewportOrigin, RenderSize); + updateTransform = false; + Center = ViewportPointToLocation(new Point(RenderSize.Width / 2d, RenderSize.Height / 2d)); + updateTransform = true; + } + else + { + scale = tileContainer.SetViewportTransform(ZoomLevel, Heading, MapTransform.Transform(Center), viewportOrigin, RenderSize); + } + + scale *= MapTransform.RelativeScale(Center) / MeterPerDegree; // Pixels per meter at center latitude + + CenterScale = scale; + scaleTransform.ScaleX = scale; + scaleTransform.ScaleY = scale; + rotateTransform.Angle = Heading; + scaleRotateTransform.Matrix = scaleTransform.Value * rotateTransform.Value; + + OnViewportChanged(); + } + } +} diff --git a/MapControl/MapControl.csproj b/MapControl/MapControl.csproj index dce34dd9..b141ef95 100644 --- a/MapControl/MapControl.csproj +++ b/MapControl/MapControl.csproj @@ -58,8 +58,8 @@ + - diff --git a/MapControl/MapElement.cs b/MapControl/MapElement.cs index 8134ff90..fca64157 100644 --- a/MapControl/MapElement.cs +++ b/MapControl/MapElement.cs @@ -19,21 +19,26 @@ namespace MapControl new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.Inherits, ParentMapPropertyChanged)); } - public Map ParentMap + public MapBase ParentMap { get { return MapPanel.GetParentMap(this); } } protected abstract void OnViewportChanged(); + private void OnViewportChanged(object sender, EventArgs e) + { + OnViewportChanged(); + } + private static void ParentMapPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e) { MapElement mapElement = obj as MapElement; if (mapElement != null) { - Map oldParentMap = e.OldValue as Map; - Map newParentMap = e.NewValue as Map; + MapBase oldParentMap = e.OldValue as MapBase; + MapBase newParentMap = e.NewValue as MapBase; if (oldParentMap != null) { diff --git a/MapControl/MapGraticule.cs b/MapControl/MapGraticule.cs index 4b0be759..2c6e3841 100644 --- a/MapControl/MapGraticule.cs +++ b/MapControl/MapGraticule.cs @@ -127,7 +127,7 @@ namespace MapControl protected override void OnViewportChanged() { - Map parentMap = ParentMap; + MapBase parentMap = ParentMap; Rect bounds = parentMap.ViewportTransform.Inverse.TransformBounds(new Rect(parentMap.RenderSize)); Location loc1 = parentMap.MapTransform.TransformBack(bounds.TopLeft); Location loc2 = parentMap.MapTransform.TransformBack(bounds.BottomRight); diff --git a/MapControl/MapItem.cs b/MapControl/MapItem.cs index 052b8da0..ff7969f5 100644 --- a/MapControl/MapItem.cs +++ b/MapControl/MapItem.cs @@ -43,7 +43,7 @@ namespace MapControl IsEnabledChanged += IsEnabledPropertyChanged; } - public Map ParentMap + public MapBase ParentMap { get { return MapPanel.GetParentMap(this); } } diff --git a/MapControl/MapItemsControl.cs b/MapControl/MapItemsControl.cs index e0127d97..846c7c9d 100644 --- a/MapControl/MapItemsControl.cs +++ b/MapControl/MapItemsControl.cs @@ -40,7 +40,7 @@ namespace MapControl Items.CurrentChanged += OnCurrentItemChanged; } - public Map ParentMap + public MapBase ParentMap { get { return MapPanel.GetParentMap(this); } } @@ -67,12 +67,12 @@ namespace MapControl element.SetValue(IsCurrentPropertyKey, value); } - public UIElement GetContainer(object item) + public UIElement ContainerFromItem(object item) { return item != null ? ItemContainerGenerator.ContainerFromItem(item) as UIElement : null; } - public object GetItem(DependencyObject container) + public object ItemFromContainer(DependencyObject container) { return container != null ? ItemContainerGenerator.ItemFromContainer(container) : null; } @@ -124,9 +124,9 @@ namespace MapControl } else if ((Keyboard.Modifiers & ModifierKeys.Shift) == 0) { - SelectedItem = GetItem(container); + SelectedItem = ItemFromContainer(container); } - else if ((selectedContainer = GetContainer(SelectedItem)) != null) + else if ((selectedContainer = ContainerFromItem(SelectedItem)) != null) { Point? p1 = MapPanel.GetViewportPosition(selectedContainer); Point? p2 = MapPanel.GetViewportPosition(container); @@ -166,7 +166,7 @@ namespace MapControl private void OnCurrentItemChanging(object sender, CurrentChangingEventArgs e) { - UIElement container = GetContainer(Items.CurrentItem); + UIElement container = ContainerFromItem(Items.CurrentItem); if (container != null) { @@ -176,7 +176,7 @@ namespace MapControl private void OnCurrentItemChanged(object sender, EventArgs e) { - UIElement container = GetContainer(Items.CurrentItem); + UIElement container = ContainerFromItem(Items.CurrentItem); if (container != null) { @@ -236,7 +236,7 @@ namespace MapControl private bool IsItemInGeometry(object item, Geometry geometry) { - UIElement container = GetContainer(item); + UIElement container = ContainerFromItem(item); Point? viewportPosition; return container != null @@ -246,7 +246,7 @@ namespace MapControl private bool IsItemInRect(object item, Rect rect) { - UIElement container = GetContainer(item); + UIElement container = ContainerFromItem(item); Point? viewportPosition; return container != null diff --git a/MapControl/MapPanel.cs b/MapControl/MapPanel.cs index 0beba283..397b4690 100644 --- a/MapControl/MapPanel.cs +++ b/MapControl/MapPanel.cs @@ -2,6 +2,7 @@ // Copyright © 2012 Clemens Fischer // Licensed under the Microsoft Public License (Ms-PL) +using System; using System.Windows; using System.Windows.Controls; @@ -15,7 +16,7 @@ namespace MapControl public class MapPanel : Panel { internal static readonly DependencyPropertyKey ParentMapPropertyKey = DependencyProperty.RegisterAttachedReadOnly( - "ParentMap", typeof(Map), typeof(MapPanel), + "ParentMap", typeof(MapBase), typeof(MapPanel), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.Inherits, ParentMapPropertyChanged)); public static readonly DependencyProperty ParentMapProperty = ParentMapPropertyKey.DependencyProperty; @@ -30,19 +31,14 @@ namespace MapControl "Location", typeof(Location), typeof(MapPanel), new FrameworkPropertyMetadata(LocationPropertyChanged)); - public MapPanel() + public MapBase ParentMap { - ClipToBounds = true; + get { return (MapBase)GetValue(ParentMapProperty); } } - public Map ParentMap + public static MapBase GetParentMap(UIElement element) { - get { return (Map)GetValue(ParentMapProperty); } - } - - public static Map GetParentMap(UIElement element) - { - return (Map)element.GetValue(ParentMapProperty); + return (MapBase)element.GetValue(ParentMapProperty); } public static Point? GetViewportPosition(UIElement element) @@ -104,14 +100,19 @@ namespace MapControl } } + private void OnViewportChanged(object sender, EventArgs e) + { + OnViewportChanged(); + } + private static void ParentMapPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e) { MapPanel mapPanel = obj as MapPanel; if (mapPanel != null) { - Map oldParentMap = e.OldValue as Map; - Map newParentMap = e.NewValue as Map; + MapBase oldParentMap = e.OldValue as MapBase; + MapBase newParentMap = e.NewValue as MapBase; if (oldParentMap != null && oldParentMap != mapPanel) { @@ -154,7 +155,7 @@ namespace MapControl } } - private static void SetViewportPosition(UIElement element, Map parentMap, Location location) + private static void SetViewportPosition(UIElement element, MapBase parentMap, Location location) { Point? viewportPosition = null; diff --git a/MapControl/MapPolyline.cs b/MapControl/MapPolyline.cs index a26bc4df..34d5245c 100644 --- a/MapControl/MapPolyline.cs +++ b/MapControl/MapPolyline.cs @@ -162,7 +162,7 @@ namespace MapControl if (TransformStroke) { - scale = ParentMap.CenterScale * Map.MeterPerDegree; + scale = ParentMap.CenterScale * MapBase.MeterPerDegree; } drawing.Pen.Thickness = scale * StrokeThickness; diff --git a/MapControl/Pushpin.cs b/MapControl/Pushpin.cs index e2bf7812..e2ef5229 100644 --- a/MapControl/Pushpin.cs +++ b/MapControl/Pushpin.cs @@ -23,7 +23,7 @@ namespace MapControl typeof(Pushpin), new FrameworkPropertyMetadata(typeof(Pushpin))); } - public Map ParentMap + public MapBase ParentMap { get { return MapPanel.GetParentMap(this); } }