mirror of
https://github.com/ClemensFischer/XAML-Map-Control.git
synced 2026-04-05 14:37:01 +00:00
First commit of the new XAML Map Control that replaces WPF Map Control, and now supports all three XAML platforms, WPF, Silverlight and WinRT.
This commit is contained in:
parent
c28e9d73d9
commit
27a302a5b8
124 changed files with 6938 additions and 2093 deletions
21
MapControl/AnimationEx.Silverlight.cs
Normal file
21
MapControl/AnimationEx.Silverlight.cs
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
// XAML Map Control - http://xamlmapcontrol.codeplex.com/
|
||||
// Copyright © 2012 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using System.Windows;
|
||||
using System.Windows.Media.Animation;
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
public static class AnimationEx
|
||||
{
|
||||
public static void BeginAnimation(this DependencyObject obj, DependencyProperty property, Timeline animation)
|
||||
{
|
||||
Storyboard.SetTargetProperty(animation, new PropertyPath(property));
|
||||
Storyboard.SetTarget(animation, obj);
|
||||
var storyboard = new Storyboard();
|
||||
storyboard.Children.Add(animation);
|
||||
storyboard.Begin();
|
||||
}
|
||||
}
|
||||
}
|
||||
49
MapControl/AnimationEx.WinRT.cs
Normal file
49
MapControl/AnimationEx.WinRT.cs
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
// XAML Map Control - http://xamlmapcontrol.codeplex.com/
|
||||
// Copyright © 2012 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Media.Animation;
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
public static class AnimationEx
|
||||
{
|
||||
public static void BeginAnimation(this DependencyObject obj, DependencyProperty property, DoubleAnimation animation)
|
||||
{
|
||||
animation.EnableDependentAnimation = true;
|
||||
|
||||
if (property == UIElement.OpacityProperty)
|
||||
{
|
||||
BeginAnimation(obj, "Opacity", animation);
|
||||
}
|
||||
else if (property == MapBase.ZoomLevelProperty)
|
||||
{
|
||||
BeginAnimation(obj, "ZoomLevel", animation);
|
||||
}
|
||||
else if (property == MapBase.HeadingProperty)
|
||||
{
|
||||
BeginAnimation(obj, "Heading", animation);
|
||||
}
|
||||
}
|
||||
|
||||
public static void BeginAnimation(this DependencyObject obj, DependencyProperty property, PointAnimation animation)
|
||||
{
|
||||
animation.EnableDependentAnimation = true;
|
||||
|
||||
if (property == MapBase.CenterPointProperty)
|
||||
{
|
||||
BeginAnimation(obj, "CenterPoint", animation);
|
||||
}
|
||||
}
|
||||
|
||||
private static void BeginAnimation(DependencyObject obj, string property, Timeline animation)
|
||||
{
|
||||
Storyboard.SetTargetProperty(animation, property);
|
||||
Storyboard.SetTarget(animation, obj);
|
||||
var storyboard = new Storyboard();
|
||||
storyboard.Children.Add(animation);
|
||||
storyboard.Begin();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
// WPF MapControl - http://wpfmapcontrol.codeplex.com/
|
||||
// XAML Map Control - http://xamlmapcontrol.codeplex.com/
|
||||
// Copyright © 2012 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
|
|
@ -22,12 +22,12 @@ namespace MapControl
|
|||
throw new ArgumentException(string.Format("{0}: no GlyphTypeface found", typeface.FontFamily));
|
||||
}
|
||||
|
||||
ushort[] glyphIndices = new ushort[text.Length];
|
||||
double[] advanceWidths = new double[text.Length];
|
||||
var glyphIndices = new ushort[text.Length];
|
||||
var advanceWidths = new double[text.Length];
|
||||
|
||||
for (int i = 0; i < text.Length; i++)
|
||||
{
|
||||
ushort glyphIndex = glyphTypeface.CharacterToGlyphMap[text[i]];
|
||||
var glyphIndex = glyphTypeface.CharacterToGlyphMap[text[i]];
|
||||
glyphIndices[i] = glyphIndex;
|
||||
advanceWidths[i] = glyphTypeface.AdvanceWidths[glyphIndex] * emSize;
|
||||
}
|
||||
|
|
@ -45,21 +45,20 @@ namespace MapControl
|
|||
throw new ArgumentException(string.Format("{0}: no GlyphTypeface found", typeface.FontFamily));
|
||||
}
|
||||
|
||||
ushort[] glyphIndices = new ushort[text.Length];
|
||||
double[] advanceWidths = new double[text.Length];
|
||||
var glyphIndices = new ushort[text.Length];
|
||||
var advanceWidths = new double[text.Length];
|
||||
|
||||
for (int i = 0; i < text.Length; i++)
|
||||
{
|
||||
ushort glyphIndex = glyphTypeface.CharacterToGlyphMap[text[i]];
|
||||
var glyphIndex = glyphTypeface.CharacterToGlyphMap[text[i]];
|
||||
glyphIndices[i] = glyphIndex;
|
||||
advanceWidths[i] = glyphTypeface.AdvanceWidths[glyphIndex] * emSize;
|
||||
}
|
||||
|
||||
GlyphRun glyphRun = new GlyphRun(glyphTypeface, 0, false, emSize, glyphIndices, new Point(), advanceWidths,
|
||||
null, null, null, null, null, null);
|
||||
|
||||
Rect bbox = glyphRun.ComputeInkBoundingBox();
|
||||
Point baselineOrigin = new Point(centerOffset.X - bbox.X - bbox.Width / 2d, centerOffset.Y - bbox.Y - bbox.Height / 2d);
|
||||
var glyphRun = new GlyphRun(glyphTypeface, 0, false, emSize, glyphIndices, new Point(), advanceWidths,
|
||||
null, null, null, null, null, null);
|
||||
var bbox = glyphRun.ComputeInkBoundingBox();
|
||||
var baselineOrigin = new Point(centerOffset.X - bbox.X - bbox.Width / 2d, centerOffset.Y - bbox.Y - bbox.Height / 2d);
|
||||
|
||||
return new GlyphRun(glyphTypeface, 0, false, emSize, glyphIndices, baselineOrigin, advanceWidths,
|
||||
null, null, null, null, null, null);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// WPF MapControl - http://wpfmapcontrol.codeplex.com/
|
||||
// XAML Map Control - http://xamlmapcontrol.codeplex.com/
|
||||
// Copyright © 2012 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
|
|
|
|||
43
MapControl/Int32Rect.cs
Normal file
43
MapControl/Int32Rect.cs
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
// XAML Map Control - http://xamlmapcontrol.codeplex.com/
|
||||
// Copyright © 2012 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
public struct Int32Rect
|
||||
{
|
||||
public Int32Rect(int x, int y, int width, int height)
|
||||
: this()
|
||||
{
|
||||
X = x;
|
||||
Y = y;
|
||||
Width = width;
|
||||
Height = height;
|
||||
}
|
||||
|
||||
public int X { get; set; }
|
||||
public int Y { get; set; }
|
||||
public int Width { get; set; }
|
||||
public int Height { get; set; }
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return X ^ Y ^ Width ^ Height;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is Int32Rect && (Int32Rect)obj == this;
|
||||
}
|
||||
|
||||
public static bool operator ==(Int32Rect rect1, Int32Rect rect2)
|
||||
{
|
||||
return rect1.X == rect2.X && rect1.Y == rect2.Y && rect1.Width == rect2.Width && rect1.Height == rect2.Height;
|
||||
}
|
||||
|
||||
public static bool operator !=(Int32Rect rect1, Int32Rect rect2)
|
||||
{
|
||||
return !(rect1 == rect2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,37 +1,39 @@
|
|||
// WPF MapControl - http://wpfmapcontrol.codeplex.com/
|
||||
// XAML Map Control - http://xamlmapcontrol.codeplex.com/
|
||||
// Copyright © 2012 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
using System.Windows;
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
/// <summary>
|
||||
/// A geographic location given as latitude and longitude.
|
||||
/// </summary>
|
||||
[TypeConverter(typeof(LocationConverter))]
|
||||
public class Location
|
||||
public partial class Location : IEquatable<Location>
|
||||
{
|
||||
private double latitude;
|
||||
private double longitude;
|
||||
internal double Y;
|
||||
|
||||
public Location()
|
||||
{
|
||||
}
|
||||
|
||||
public Location(double latitude, double longitude)
|
||||
public Location(double lat, double lon)
|
||||
{
|
||||
Latitude = latitude;
|
||||
Longitude = longitude;
|
||||
Latitude = lat;
|
||||
Longitude = lon;
|
||||
}
|
||||
|
||||
public double Latitude
|
||||
{
|
||||
get { return latitude; }
|
||||
set { latitude = Math.Min(Math.Max(value, -90d), 90d); }
|
||||
set
|
||||
{
|
||||
latitude = Math.Min(Math.Max(value, -90d), 90d);
|
||||
Y = double.NaN;
|
||||
}
|
||||
}
|
||||
|
||||
public double Longitude
|
||||
|
|
@ -40,36 +42,42 @@ namespace MapControl
|
|||
set { longitude = value; }
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
public bool Equals(Location other)
|
||||
{
|
||||
return string.Format(CultureInfo.InvariantCulture, "{0:0.00000},{1:0.00000}", latitude, longitude);
|
||||
return other != null && other.latitude == latitude && other.longitude == longitude;
|
||||
}
|
||||
|
||||
public static Location Parse(string source)
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
Point p = Point.Parse(source);
|
||||
return new Location(p.X, p.Y);
|
||||
return Equals(obj as Location);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return latitude.GetHashCode() ^ longitude.GetHashCode();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format(CultureInfo.InvariantCulture, "{0:F5},{1:F5}", latitude, longitude);
|
||||
}
|
||||
|
||||
public static Location Parse(string s)
|
||||
{
|
||||
var tokens = s.Split(new char[] { ',' });
|
||||
if (tokens.Length != 2)
|
||||
{
|
||||
throw new FormatException("Location string must be a comma-separated pair of double values");
|
||||
}
|
||||
|
||||
return new Location(
|
||||
double.Parse(tokens[0], NumberStyles.Float, CultureInfo.InvariantCulture),
|
||||
double.Parse(tokens[1], NumberStyles.Float, CultureInfo.InvariantCulture));
|
||||
}
|
||||
|
||||
public static double NormalizeLongitude(double longitude)
|
||||
{
|
||||
return ((longitude + 180d) % 360d + 360d) % 360d - 180d;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts from string to Location.
|
||||
/// </summary>
|
||||
public class LocationConverter : TypeConverter
|
||||
{
|
||||
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
|
||||
{
|
||||
return sourceType == typeof(string);
|
||||
}
|
||||
|
||||
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
|
||||
{
|
||||
return Location.Parse((string)value);
|
||||
return (longitude >= -180d && longitude <= 180d) ? longitude : ((longitude + 180d) % 360d + 360d) % 360d - 180d;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,56 +0,0 @@
|
|||
// WPF MapControl - http://wpfmapcontrol.codeplex.com/
|
||||
// Copyright © 2012 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using System;
|
||||
using System.Windows;
|
||||
using System.Windows.Media.Animation;
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
/// <summary>
|
||||
/// Animates the value of a Location property between two values.
|
||||
/// </summary>
|
||||
public class LocationAnimation : AnimationTimeline
|
||||
{
|
||||
public Location From { get; set; }
|
||||
public Location To { get; set; }
|
||||
public IEasingFunction EasingFunction { get; set; }
|
||||
|
||||
public override Type TargetPropertyType
|
||||
{
|
||||
get { return typeof(Location); }
|
||||
}
|
||||
|
||||
protected override Freezable CreateInstanceCore()
|
||||
{
|
||||
return new LocationAnimation
|
||||
{
|
||||
From = From,
|
||||
To = To,
|
||||
EasingFunction = EasingFunction
|
||||
};
|
||||
}
|
||||
|
||||
public override object GetCurrentValue(object defaultOriginValue, object defaultDestinationValue, AnimationClock animationClock)
|
||||
{
|
||||
if (!animationClock.CurrentProgress.HasValue)
|
||||
{
|
||||
return defaultOriginValue;
|
||||
}
|
||||
|
||||
double progress = animationClock.CurrentProgress.Value;
|
||||
|
||||
if (EasingFunction != null)
|
||||
{
|
||||
progress = EasingFunction.Ease(progress);
|
||||
}
|
||||
|
||||
double deltaLongitude = progress * Location.NormalizeLongitude(To.Longitude - From.Longitude);
|
||||
|
||||
return new Location(
|
||||
(1d - progress) * From.Latitude + progress * To.Latitude,
|
||||
Location.NormalizeLongitude(From.Longitude + deltaLongitude));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,20 +1,17 @@
|
|||
// WPF MapControl - http://wpfmapcontrol.codeplex.com/
|
||||
// XAML Map Control - http://xamlmapcontrol.codeplex.com/
|
||||
// Copyright © 2012 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
/// <summary>
|
||||
/// A collection of geographic locations.
|
||||
/// </summary>
|
||||
[TypeConverter(typeof(LocationCollectionConverter))]
|
||||
public class LocationCollection : ObservableCollection<Location>
|
||||
public partial class LocationCollection : ObservableCollection<Location>
|
||||
{
|
||||
public LocationCollection()
|
||||
{
|
||||
|
|
@ -28,11 +25,11 @@ namespace MapControl
|
|||
}
|
||||
}
|
||||
|
||||
public static LocationCollection Parse(string source)
|
||||
public static LocationCollection Parse(string s)
|
||||
{
|
||||
LocationCollection locations = new LocationCollection();
|
||||
|
||||
foreach (string locString in source.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries))
|
||||
foreach (var locString in s.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries))
|
||||
{
|
||||
locations.Add(Location.Parse(locString));
|
||||
}
|
||||
|
|
@ -40,20 +37,4 @@ namespace MapControl
|
|||
return locations;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts from string to LocationCollection.
|
||||
/// </summary>
|
||||
public class LocationCollectionConverter : TypeConverter
|
||||
{
|
||||
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
|
||||
{
|
||||
return sourceType == typeof(string);
|
||||
}
|
||||
|
||||
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
|
||||
{
|
||||
return LocationCollection.Parse((string)value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
28
MapControl/LocationCollectionConverter.cs
Normal file
28
MapControl/LocationCollectionConverter.cs
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
// XAML Map Control - http://xamlmapcontrol.codeplex.com/
|
||||
// Copyright © 2012 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
public class LocationCollectionConverter : TypeConverter
|
||||
{
|
||||
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
|
||||
{
|
||||
return sourceType == typeof(string);
|
||||
}
|
||||
|
||||
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
|
||||
{
|
||||
return LocationCollection.Parse((string)value);
|
||||
}
|
||||
}
|
||||
|
||||
[TypeConverter(typeof(LocationCollectionConverter))]
|
||||
public partial class LocationCollection
|
||||
{
|
||||
}
|
||||
}
|
||||
28
MapControl/LocationConverter.cs
Normal file
28
MapControl/LocationConverter.cs
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
// XAML Map Control - http://xamlmapcontrol.codeplex.com/
|
||||
// Copyright © 2012 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
public class LocationConverter : TypeConverter
|
||||
{
|
||||
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
|
||||
{
|
||||
return sourceType == typeof(string);
|
||||
}
|
||||
|
||||
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
|
||||
{
|
||||
return Location.Parse((string)value);
|
||||
}
|
||||
}
|
||||
|
||||
[TypeConverter(typeof(LocationConverter))]
|
||||
public partial class Location
|
||||
{
|
||||
}
|
||||
}
|
||||
64
MapControl/Map.Silverlight.WPF.cs
Normal file
64
MapControl/Map.Silverlight.WPF.cs
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
// XAML Map Control - http://xamlmapcontrol.codeplex.com/
|
||||
// Copyright © 2012 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using System;
|
||||
using System.Windows;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
public partial class Map
|
||||
{
|
||||
partial void Initialize()
|
||||
{
|
||||
#if !SILVERLIGHT
|
||||
ManipulationDelta += OnManipulationDelta;
|
||||
#endif
|
||||
MouseWheel += OnMouseWheel;
|
||||
MouseLeftButtonDown += OnMouseLeftButtonDown;
|
||||
MouseLeftButtonUp += OnMouseLeftButtonUp;
|
||||
MouseMove += OnMouseMove;
|
||||
}
|
||||
|
||||
#if !SILVERLIGHT
|
||||
private void OnManipulationDelta(object sender, ManipulationDeltaEventArgs e)
|
||||
{
|
||||
var d = e.DeltaManipulation;
|
||||
TransformMap(e.ManipulationOrigin, (Point)d.Translation, d.Rotation, (d.Scale.X + d.Scale.Y) / 2d);
|
||||
}
|
||||
#endif
|
||||
|
||||
private void OnMouseWheel(object sender, MouseWheelEventArgs e)
|
||||
{
|
||||
ZoomMap(e.GetPosition(this), TargetZoomLevel + mouseWheelZoom * Math.Sign(e.Delta));
|
||||
}
|
||||
|
||||
private void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
if (CaptureMouse())
|
||||
{
|
||||
mousePosition = e.GetPosition(this);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
if (mousePosition.HasValue)
|
||||
{
|
||||
mousePosition = null;
|
||||
ReleaseMouseCapture();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnMouseMove(object sender, MouseEventArgs e)
|
||||
{
|
||||
if (mousePosition.HasValue)
|
||||
{
|
||||
var position = e.GetPosition(this);
|
||||
TranslateMap(new Point(position.X - mousePosition.Value.X, position.Y - mousePosition.Value.Y));
|
||||
mousePosition = position;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
68
MapControl/Map.WinRT.cs
Normal file
68
MapControl/Map.WinRT.cs
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
// XAML Map Control - http://xamlmapcontrol.codeplex.com/
|
||||
// Copyright © 2012 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using System;
|
||||
using Windows.Devices.Input;
|
||||
using Windows.Foundation;
|
||||
using Windows.UI.Xaml.Input;
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
public partial class Map
|
||||
{
|
||||
partial void Initialize()
|
||||
{
|
||||
ManipulationMode = ManipulationModes.All;
|
||||
ManipulationDelta += OnManipulationDelta;
|
||||
PointerWheelChanged += OnPointerWheelChanged;
|
||||
PointerPressed += OnPointerPressed;
|
||||
PointerReleased += OnPointerReleased;
|
||||
PointerCanceled += OnPointerReleased;
|
||||
PointerCaptureLost += OnPointerReleased;
|
||||
PointerMoved += OnPointerMoved;
|
||||
}
|
||||
|
||||
private void OnManipulationDelta(object sender, ManipulationDeltaRoutedEventArgs e)
|
||||
{
|
||||
if (e.PointerDeviceType != PointerDeviceType.Mouse)
|
||||
{
|
||||
TransformMap(e.Position, e.Delta.Translation, e.Delta.Rotation, e.Delta.Scale);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnPointerWheelChanged(object sender, PointerRoutedEventArgs e)
|
||||
{
|
||||
var point = e.GetCurrentPoint(this);
|
||||
ZoomMap(point.Position, TargetZoomLevel + mouseWheelZoom * Math.Sign(point.Properties.MouseWheelDelta));
|
||||
}
|
||||
|
||||
private void OnPointerPressed(object sender, PointerRoutedEventArgs e)
|
||||
{
|
||||
if (e.Pointer.PointerDeviceType == PointerDeviceType.Mouse &&
|
||||
CapturePointer(e.Pointer))
|
||||
{
|
||||
mousePosition = e.GetCurrentPoint(this).Position;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnPointerReleased(object sender, PointerRoutedEventArgs e)
|
||||
{
|
||||
if (e.Pointer.PointerDeviceType == PointerDeviceType.Mouse)
|
||||
{
|
||||
mousePosition = null;
|
||||
ReleasePointerCapture(e.Pointer);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnPointerMoved(object sender, PointerRoutedEventArgs e)
|
||||
{
|
||||
if (mousePosition.HasValue)
|
||||
{
|
||||
var position = e.GetCurrentPoint(this).Position;
|
||||
TranslateMap(new Point(position.X - mousePosition.Value.X, position.Y - mousePosition.Value.Y));
|
||||
mousePosition = position;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,88 +1,34 @@
|
|||
// WPF MapControl - http://wpfmapcontrol.codeplex.com/
|
||||
// XAML Map Control - http://xamlmapcontrol.codeplex.com/
|
||||
// Copyright © 2012 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using System;
|
||||
#if WINRT
|
||||
using Windows.Foundation;
|
||||
#else
|
||||
using System.Windows;
|
||||
using System.Windows.Input;
|
||||
#endif
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
/// <summary>
|
||||
/// MapBase with input event handling.
|
||||
/// </summary>
|
||||
public class Map : MapBase
|
||||
public partial class Map : MapBase
|
||||
{
|
||||
private double mouseWheelZoom = 1d;
|
||||
private Point? mousePosition;
|
||||
|
||||
public Map()
|
||||
{
|
||||
Initialize();
|
||||
}
|
||||
|
||||
partial void Initialize();
|
||||
|
||||
public double MouseWheelZoom
|
||||
{
|
||||
get { return mouseWheelZoom; }
|
||||
set { mouseWheelZoom = value; }
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
ZoomMap(e.GetPosition(this), Math.Ceiling(ZoomLevel - 1.5));
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
|
||||
{
|
||||
base.OnMouseLeftButtonDown(e);
|
||||
|
||||
if (e.ClickCount == 1)
|
||||
{
|
||||
mousePosition = e.GetPosition(this);
|
||||
CaptureMouse();
|
||||
}
|
||||
else if (e.ClickCount == 2)
|
||||
{
|
||||
ZoomMap(e.GetPosition(this), Math.Floor(ZoomLevel + 1.5));
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)
|
||||
{
|
||||
base.OnMouseLeftButtonUp(e);
|
||||
|
||||
if (mousePosition.HasValue)
|
||||
{
|
||||
mousePosition = null;
|
||||
ReleaseMouseCapture();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnMouseMove(MouseEventArgs e)
|
||||
{
|
||||
base.OnMouseMove(e);
|
||||
|
||||
if (mousePosition.HasValue)
|
||||
{
|
||||
Point position = e.GetPosition(this);
|
||||
TranslateMap(position - mousePosition.Value);
|
||||
mousePosition = position;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnManipulationDelta(ManipulationDeltaEventArgs e)
|
||||
{
|
||||
base.OnManipulationDelta(e);
|
||||
|
||||
ManipulationDelta d = e.DeltaManipulation;
|
||||
TransformMap(e.ManipulationOrigin, d.Translation, d.Rotation, (d.Scale.X + d.Scale.Y) / 2d);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
49
MapControl/MapBase.Silverlight.WinRT.cs
Normal file
49
MapControl/MapBase.Silverlight.WinRT.cs
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
// XAML Map Control - http://xamlmapcontrol.codeplex.com/
|
||||
// Copyright © 2012 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
#if WINRT
|
||||
using Windows.Foundation;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Media;
|
||||
using Windows.UI.Xaml.Media.Animation;
|
||||
#else
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Animation;
|
||||
#endif
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
public partial class MapBase
|
||||
{
|
||||
// Set FillBehavior.HoldEnd to prevent animation from returning
|
||||
// to local value before invoking the Completed handler
|
||||
private const FillBehavior AnimationFillBehavior = FillBehavior.HoldEnd;
|
||||
|
||||
public static readonly DependencyProperty ForegroundProperty = DependencyProperty.Register(
|
||||
"Foreground", typeof(Brush), typeof(MapBase), null);
|
||||
|
||||
partial void Initialize()
|
||||
{
|
||||
Clip = new RectangleGeometry();
|
||||
Children.Add(tileContainer);
|
||||
|
||||
SizeChanged += OnRenderSizeChanged;
|
||||
}
|
||||
|
||||
private void OnRenderSizeChanged(object sender, SizeChangedEventArgs e)
|
||||
{
|
||||
((RectangleGeometry)Clip).Rect = new Rect(0d, 0d, RenderSize.Width, RenderSize.Height);
|
||||
ResetTransformOrigin();
|
||||
UpdateTransform();
|
||||
}
|
||||
|
||||
private void SetTransformMatrixes(double scale)
|
||||
{
|
||||
scaleTransform.Matrix = new Matrix(scale, 0d, 0d, scale, 0d, 0d);
|
||||
rotateTransform.Matrix = Matrix.Identity.Rotate(Heading);
|
||||
scaleRotateTransform.Matrix = scaleTransform.Matrix.Multiply(rotateTransform.Matrix);
|
||||
}
|
||||
}
|
||||
}
|
||||
66
MapControl/MapBase.WPF.cs
Normal file
66
MapControl/MapBase.WPF.cs
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
// XAML Map Control - http://xamlmapcontrol.codeplex.com/
|
||||
// Copyright © 2012 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Animation;
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
public partial class MapBase
|
||||
{
|
||||
// FillBehavior must be set to Stop to re-enable local property values
|
||||
private const FillBehavior AnimationFillBehavior = FillBehavior.Stop;
|
||||
|
||||
public static readonly DependencyProperty ForegroundProperty =
|
||||
System.Windows.Controls.Control.ForegroundProperty.AddOwner(typeof(MapBase));
|
||||
|
||||
static MapBase()
|
||||
{
|
||||
UIElement.ClipToBoundsProperty.OverrideMetadata(
|
||||
typeof(MapBase), new FrameworkPropertyMetadata(true));
|
||||
|
||||
Panel.BackgroundProperty.OverrideMetadata(
|
||||
typeof(MapBase), new FrameworkPropertyMetadata(Brushes.Transparent));
|
||||
}
|
||||
|
||||
partial void Initialize()
|
||||
{
|
||||
AddVisualChild(tileContainer);
|
||||
|
||||
SizeChanged += OnRenderSizeChanged;
|
||||
}
|
||||
|
||||
private void OnRenderSizeChanged(object sender, SizeChangedEventArgs e)
|
||||
{
|
||||
ResetTransformOrigin();
|
||||
UpdateTransform();
|
||||
}
|
||||
|
||||
private void SetTransformMatrixes(double scale)
|
||||
{
|
||||
Matrix rotateMatrix = Matrix.Identity;
|
||||
rotateMatrix.Rotate(Heading);
|
||||
rotateTransform.Matrix = rotateMatrix;
|
||||
scaleTransform.Matrix = new Matrix(scale, 0d, 0d, scale, 0d, 0d);
|
||||
scaleRotateTransform.Matrix = scaleTransform.Matrix * rotateMatrix;
|
||||
}
|
||||
|
||||
protected override int VisualChildrenCount
|
||||
{
|
||||
get { return InternalChildren.Count + 1; }
|
||||
}
|
||||
|
||||
protected override Visual GetVisualChild(int index)
|
||||
{
|
||||
if (index == 0)
|
||||
{
|
||||
return tileContainer;
|
||||
}
|
||||
|
||||
return InternalChildren[index - 1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,170 +1,127 @@
|
|||
// WPF MapControl - http://wpfmapcontrol.codeplex.com/
|
||||
// XAML Map Control - http://xamlmapcontrol.codeplex.com/
|
||||
// Copyright © 2012 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using System;
|
||||
using System.Collections.Specialized;
|
||||
using System.Linq;
|
||||
#if WINRT
|
||||
using Windows.Foundation;
|
||||
using Windows.UI;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Media;
|
||||
using Windows.UI.Xaml.Media.Animation;
|
||||
#else
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Animation;
|
||||
#endif
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
/// <summary>
|
||||
/// The map control. Draws map content provided by the TileLayers or the BaseTileLayer property.
|
||||
/// The map control. Draws map content provided by the TileLayers or the TileLayer 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.
|
||||
/// </summary>
|
||||
public class MapBase : MapPanel
|
||||
public partial 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 TimeSpan AnimationDuration = TimeSpan.FromSeconds(0.5);
|
||||
public static EasingFunctionBase AnimationEasingFunction = new QuadraticEase { EasingMode = EasingMode.EaseOut };
|
||||
|
||||
public static readonly DependencyProperty LightForegroundProperty = DependencyProperty.Register(
|
||||
"LightForeground", typeof(Brush), typeof(MapBase));
|
||||
"LightForeground", typeof(Brush), typeof(MapBase), null);
|
||||
|
||||
public static readonly DependencyProperty DarkForegroundProperty = DependencyProperty.Register(
|
||||
"DarkForeground", typeof(Brush), typeof(MapBase));
|
||||
"DarkForeground", typeof(Brush), typeof(MapBase), null);
|
||||
|
||||
public static readonly DependencyProperty LightBackgroundProperty = DependencyProperty.Register(
|
||||
"LightBackground", typeof(Brush), typeof(MapBase));
|
||||
"LightBackground", typeof(Brush), typeof(MapBase), new PropertyMetadata(new SolidColorBrush(Colors.Transparent), null));
|
||||
|
||||
public static readonly DependencyProperty DarkBackgroundProperty = DependencyProperty.Register(
|
||||
"DarkBackground", typeof(Brush), typeof(MapBase));
|
||||
"DarkBackground", typeof(Brush), typeof(MapBase), new PropertyMetadata(new SolidColorBrush(Colors.Transparent), null));
|
||||
|
||||
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)));
|
||||
"TileLayers", typeof(TileLayerCollection), typeof(MapBase), new PropertyMetadata(null,
|
||||
(o, e) => ((MapBase)o).TileLayersPropertyChanged((TileLayerCollection)e.OldValue, (TileLayerCollection)e.NewValue)));
|
||||
|
||||
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 TileLayerProperty = DependencyProperty.Register(
|
||||
"TileLayer", typeof(TileLayer), typeof(MapBase), new PropertyMetadata(null,
|
||||
(o, e) => ((MapBase)o).TileLayerPropertyChanged((TileLayer)e.NewValue)));
|
||||
|
||||
public static readonly DependencyProperty TileOpacityProperty = DependencyProperty.Register(
|
||||
"TileOpacity", typeof(double), typeof(MapBase), new FrameworkPropertyMetadata(1d,
|
||||
"TileOpacity", typeof(double), typeof(MapBase), new PropertyMetadata(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)));
|
||||
"Center", typeof(Location), typeof(MapBase), new PropertyMetadata(new Location(),
|
||||
(o, e) => ((MapBase)o).CenterPropertyChanged((Location)e.NewValue)));
|
||||
|
||||
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)));
|
||||
"TargetCenter", typeof(Location), typeof(MapBase), new PropertyMetadata(new Location(),
|
||||
(o, e) => ((MapBase)o).TargetCenterPropertyChanged((Location)e.NewValue)));
|
||||
|
||||
internal static readonly DependencyProperty CenterPointProperty = DependencyProperty.Register(
|
||||
"CenterPoint", typeof(Point), typeof(MapBase), new PropertyMetadata(new Point(),
|
||||
(o, e) => ((MapBase)o).CenterPointPropertyChanged((Point)e.NewValue)));
|
||||
|
||||
public static readonly DependencyProperty MinZoomLevelProperty = DependencyProperty.Register(
|
||||
"MinZoomLevel", typeof(double), typeof(MapBase), new PropertyMetadata(1d,
|
||||
(o, e) => ((MapBase)o).MinZoomLevelPropertyChanged((double)e.NewValue)));
|
||||
|
||||
public static readonly DependencyProperty MaxZoomLevelProperty = DependencyProperty.Register(
|
||||
"MaxZoomLevel", typeof(double), typeof(MapBase), new PropertyMetadata(18d,
|
||||
(o, e) => ((MapBase)o).MaxZoomLevelPropertyChanged((double)e.NewValue)));
|
||||
|
||||
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)));
|
||||
"ZoomLevel", typeof(double), typeof(MapBase), new PropertyMetadata(1d,
|
||||
(o, e) => ((MapBase)o).ZoomLevelPropertyChanged((double)e.NewValue)));
|
||||
|
||||
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)));
|
||||
"TargetZoomLevel", typeof(double), typeof(MapBase), new PropertyMetadata(1d,
|
||||
(o, e) => ((MapBase)o).TargetZoomLevelPropertyChanged((double)e.NewValue)));
|
||||
|
||||
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)));
|
||||
"Heading", typeof(double), typeof(MapBase), new PropertyMetadata(0d,
|
||||
(o, e) => ((MapBase)o).HeadingPropertyChanged((double)e.NewValue)));
|
||||
|
||||
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)));
|
||||
"TargetHeading", typeof(double), typeof(MapBase), new PropertyMetadata(0d,
|
||||
(o, e) => ((MapBase)o).TargetHeadingPropertyChanged((double)e.NewValue)));
|
||||
|
||||
private static readonly DependencyPropertyKey CenterScalePropertyKey = DependencyProperty.RegisterReadOnly(
|
||||
public static readonly DependencyProperty CenterScaleProperty = DependencyProperty.Register(
|
||||
"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 scaleTransform = new MatrixTransform();
|
||||
private readonly MatrixTransform rotateTransform = new MatrixTransform();
|
||||
private readonly MatrixTransform scaleRotateTransform = new MatrixTransform();
|
||||
private Location transformOrigin;
|
||||
private Point viewportOrigin;
|
||||
private LocationAnimation centerAnimation;
|
||||
private PointAnimation centerAnimation;
|
||||
private DoubleAnimation zoomLevelAnimation;
|
||||
private DoubleAnimation headingAnimation;
|
||||
private bool updateTransform = true;
|
||||
private bool internalPropertyChange;
|
||||
|
||||
public MapBase()
|
||||
{
|
||||
ClipToBounds = true;
|
||||
MinZoomLevel = 1;
|
||||
MaxZoomLevel = 20;
|
||||
|
||||
AddVisualChild(tileContainer);
|
||||
Background = LightBackground;
|
||||
TileLayers = new TileLayerCollection();
|
||||
Initialize();
|
||||
|
||||
SetValue(ParentMapPropertyKey, this);
|
||||
|
||||
Loaded += (o, e) =>
|
||||
{
|
||||
if (BaseTileLayer == null)
|
||||
{
|
||||
BaseTileLayer = new TileLayer
|
||||
{
|
||||
SourceName = "OpenStreetMap",
|
||||
Description = "© {y} OpenStreetMap Contributors, CC-BY-SA",
|
||||
TileSource = new TileSource("http://{c}.tile.openstreetmap.org/{z}/{x}/{y}.png")
|
||||
};
|
||||
}
|
||||
};
|
||||
Loaded += OnLoaded;
|
||||
}
|
||||
|
||||
partial void Initialize();
|
||||
|
||||
/// <summary>
|
||||
/// Raised when the current viewport has changed.
|
||||
/// </summary>
|
||||
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); }
|
||||
|
|
@ -207,10 +164,10 @@ namespace MapControl
|
|||
/// <summary>
|
||||
/// Gets or sets the base TileLayer used by this Map, i.e. TileLayers[0].
|
||||
/// </summary>
|
||||
public TileLayer BaseTileLayer
|
||||
public TileLayer TileLayer
|
||||
{
|
||||
get { return (TileLayer)GetValue(BaseTileLayerProperty); }
|
||||
set { SetValue(BaseTileLayerProperty, value); }
|
||||
get { return (TileLayer)GetValue(TileLayerProperty); }
|
||||
set { SetValue(TileLayerProperty, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -240,6 +197,26 @@ namespace MapControl
|
|||
set { SetValue(TargetCenterProperty, value); }
|
||||
}
|
||||
|
||||
/// <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.
|
||||
/// </summary>
|
||||
public double MinZoomLevel
|
||||
{
|
||||
get { return (double)GetValue(MinZoomLevelProperty); }
|
||||
set { SetValue(MinZoomLevelProperty, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the maximum value of the ZoomLevel and TargetZommLevel properties.
|
||||
/// Must be greater than or equal to MinZoomLevel and less than or equal to 20.
|
||||
/// </summary>
|
||||
public double MaxZoomLevel
|
||||
{
|
||||
get { return (double)GetValue(MaxZoomLevelProperty); }
|
||||
set { SetValue(MaxZoomLevelProperty, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the map zoom level.
|
||||
/// </summary>
|
||||
|
|
@ -282,7 +259,7 @@ namespace MapControl
|
|||
public double CenterScale
|
||||
{
|
||||
get { return (double)GetValue(CenterScaleProperty); }
|
||||
private set { SetValue(CenterScalePropertyKey, value); }
|
||||
private set { SetValue(CenterScaleProperty, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -326,12 +303,20 @@ namespace MapControl
|
|||
get { return scaleRotateTransform; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the map scale at the specified location as viewport coordinate units (pixels) per meter.
|
||||
/// </summary>
|
||||
public double GetMapScale(Location location)
|
||||
{
|
||||
return mapTransform.RelativeScale(location) * Math.Pow(2d, ZoomLevel) * 256d / (MeterPerDegree * 360d);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Transforms a geographic location to a viewport coordinates point.
|
||||
/// </summary>
|
||||
public Point LocationToViewportPoint(Location location)
|
||||
{
|
||||
return ViewportTransform.Transform(MapTransform.Transform(location));
|
||||
return ViewportTransform.Transform(mapTransform.Transform(location));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -339,7 +324,7 @@ namespace MapControl
|
|||
/// </summary>
|
||||
public Location ViewportPointToLocation(Point point)
|
||||
{
|
||||
return MapTransform.TransformBack(ViewportTransform.Inverse.Transform(point));
|
||||
return mapTransform.Transform(ViewportTransform.Inverse.Transform(point));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -360,7 +345,7 @@ namespace MapControl
|
|||
{
|
||||
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));
|
||||
transformOrigin = CoerceLocation(ViewportPointToLocation(viewportOrigin));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -375,18 +360,19 @@ namespace MapControl
|
|||
/// <summary>
|
||||
/// Changes the Center property according to the specified translation in viewport coordinates.
|
||||
/// </summary>
|
||||
public void TranslateMap(Vector translation)
|
||||
public void TranslateMap(Point translation)
|
||||
{
|
||||
if (translation.X != 0d || translation.Y != 0d)
|
||||
{
|
||||
if (transformOrigin != null)
|
||||
{
|
||||
viewportOrigin += translation;
|
||||
viewportOrigin.X += translation.X;
|
||||
viewportOrigin.Y += translation.Y;
|
||||
UpdateTransform();
|
||||
}
|
||||
else
|
||||
{
|
||||
Center = ViewportPointToLocation(viewportOrigin - translation);
|
||||
Center = ViewportPointToLocation(new Point(viewportOrigin.X - translation.X, viewportOrigin.Y - translation.Y));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -396,15 +382,13 @@ namespace MapControl
|
|||
/// viewport coordinate translation, rotation and scale delta values. Rotation and scaling
|
||||
/// is performed relative to the specified origin point in viewport coordinates.
|
||||
/// </summary>
|
||||
public void TransformMap(Point origin, Vector translation, double rotation, double scale)
|
||||
public void TransformMap(Point origin, Point 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;
|
||||
SetProperty(HeadingProperty, CoerceHeading(Heading + rotation));
|
||||
SetProperty(ZoomLevelProperty, CoerceZoomLevel(ZoomLevel + Math.Log(scale, 2d)));
|
||||
UpdateTransform();
|
||||
}
|
||||
|
||||
|
|
@ -418,7 +402,7 @@ namespace MapControl
|
|||
/// </summary>
|
||||
public void ZoomMap(Point origin, double zoomLevel)
|
||||
{
|
||||
double targetZoomLebel = TargetZoomLevel;
|
||||
var targetZoomLebel = TargetZoomLevel;
|
||||
TargetZoomLevel = zoomLevel;
|
||||
|
||||
if (TargetZoomLevel != targetZoomLebel) // TargetZoomLevel might be coerced
|
||||
|
|
@ -427,37 +411,6 @@ namespace MapControl
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the map scale at the specified location as viewport coordinate units (pixels) per meter.
|
||||
/// </summary>
|
||||
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 OnViewportChanged()
|
||||
{
|
||||
base.OnViewportChanged();
|
||||
|
|
@ -468,6 +421,18 @@ namespace MapControl
|
|||
}
|
||||
}
|
||||
|
||||
private void OnLoaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Loaded -= OnLoaded;
|
||||
|
||||
if (TileLayer == null)
|
||||
{
|
||||
TileLayer = TileLayer.Default;
|
||||
}
|
||||
|
||||
UpdateTransform();
|
||||
}
|
||||
|
||||
private void TileLayerCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
switch (e.Action)
|
||||
|
|
@ -477,12 +442,13 @@ namespace MapControl
|
|||
break;
|
||||
|
||||
case NotifyCollectionChangedAction.Remove:
|
||||
tileContainer.RemoveTileLayers(e.OldStartingIndex, e.OldItems.Cast<TileLayer>());
|
||||
tileContainer.RemoveTileLayers(e.OldStartingIndex, e.OldItems.Count);
|
||||
break;
|
||||
|
||||
#if !SILVERLIGHT
|
||||
case NotifyCollectionChangedAction.Move:
|
||||
#endif
|
||||
case NotifyCollectionChangedAction.Replace:
|
||||
tileContainer.RemoveTileLayers(e.OldStartingIndex, e.OldItems.Cast<TileLayer>());
|
||||
tileContainer.RemoveTileLayers(e.NewStartingIndex, e.OldItems.Count);
|
||||
tileContainer.AddTileLayers(e.NewStartingIndex, e.NewItems.Cast<TileLayer>());
|
||||
break;
|
||||
|
||||
|
|
@ -493,9 +459,12 @@ namespace MapControl
|
|||
tileContainer.AddTileLayers(0, e.NewItems.Cast<TileLayer>());
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
UpdateBaseTileLayer();
|
||||
UpdateTileLayer();
|
||||
}
|
||||
|
||||
private void TileLayersPropertyChanged(TileLayerCollection oldTileLayers, TileLayerCollection newTileLayers)
|
||||
|
|
@ -513,34 +482,29 @@ namespace MapControl
|
|||
tileContainer.AddTileLayers(0, newTileLayers);
|
||||
}
|
||||
|
||||
UpdateBaseTileLayer();
|
||||
UpdateTileLayer();
|
||||
}
|
||||
|
||||
private TileLayerCollection CoerceTileLayersProperty(TileLayerCollection tileLayers)
|
||||
private void TileLayerPropertyChanged(TileLayer tileLayer)
|
||||
{
|
||||
if (tileLayers == null)
|
||||
if (tileLayer != null)
|
||||
{
|
||||
tileLayers = new TileLayerCollection();
|
||||
}
|
||||
if (TileLayers == null)
|
||||
{
|
||||
TileLayers = new TileLayerCollection();
|
||||
}
|
||||
|
||||
return tileLayers;
|
||||
}
|
||||
|
||||
private void BaseTileLayerPropertyChanged(TileLayer baseTileLayer)
|
||||
{
|
||||
if (baseTileLayer != null)
|
||||
{
|
||||
if (TileLayers.Count == 0)
|
||||
{
|
||||
TileLayers.Add(baseTileLayer);
|
||||
TileLayers.Add(tileLayer);
|
||||
}
|
||||
else if (TileLayers[0] != baseTileLayer)
|
||||
else if (TileLayers[0] != tileLayer)
|
||||
{
|
||||
TileLayers[0] = baseTileLayer;
|
||||
TileLayers[0] = tileLayer;
|
||||
}
|
||||
}
|
||||
|
||||
if (baseTileLayer != null && baseTileLayer.HasDarkBackground)
|
||||
if (tileLayer != null && tileLayer.HasDarkBackground)
|
||||
{
|
||||
if (DarkForeground != null)
|
||||
{
|
||||
|
|
@ -566,93 +530,175 @@ namespace MapControl
|
|||
}
|
||||
}
|
||||
|
||||
private TileLayer CoerceBaseTileLayerProperty(TileLayer baseTileLayer)
|
||||
private void UpdateTileLayer()
|
||||
{
|
||||
if (baseTileLayer == null && TileLayers.Count > 0)
|
||||
{
|
||||
baseTileLayer = TileLayers[0];
|
||||
}
|
||||
var tileLayer = TileLayers.FirstOrDefault();
|
||||
|
||||
return baseTileLayer;
|
||||
if (TileLayer != tileLayer)
|
||||
{
|
||||
TileLayer = tileLayer;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateBaseTileLayer()
|
||||
private void SetProperty(DependencyProperty property, object value)
|
||||
{
|
||||
TileLayer baseTileLayer = TileLayers.FirstOrDefault();
|
||||
internalPropertyChange = true;
|
||||
SetValue(property, value);
|
||||
internalPropertyChange = false;
|
||||
}
|
||||
|
||||
if (BaseTileLayer != baseTileLayer)
|
||||
private Location CoerceLocation(Location location)
|
||||
{
|
||||
return new Location(
|
||||
Math.Min(Math.Max(location.Latitude, -mapTransform.MaxLatitude), mapTransform.MaxLatitude),
|
||||
Location.NormalizeLongitude(location.Longitude));
|
||||
}
|
||||
|
||||
private bool CoerceCenterProperty(DependencyProperty property, ref Location value)
|
||||
{
|
||||
Location coercedValue = CoerceLocation(value);
|
||||
|
||||
if (!coercedValue.Equals(value))
|
||||
{
|
||||
BaseTileLayer = baseTileLayer;
|
||||
SetProperty(property, coercedValue);
|
||||
}
|
||||
|
||||
return coercedValue != value;
|
||||
}
|
||||
|
||||
private void CenterPropertyChanged(Location center)
|
||||
{
|
||||
if (updateTransform)
|
||||
if (!internalPropertyChange)
|
||||
{
|
||||
CoerceCenterProperty(CenterProperty, ref center);
|
||||
ResetTransformOrigin();
|
||||
UpdateTransform();
|
||||
}
|
||||
|
||||
if (centerAnimation == null)
|
||||
{
|
||||
TargetCenter = center;
|
||||
if (centerAnimation == null)
|
||||
{
|
||||
SetProperty(TargetCenterProperty, center);
|
||||
SetProperty(CenterPointProperty, new Point(center.Longitude, center.Latitude));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void TargetCenterPropertyChanged(Location targetCenter)
|
||||
{
|
||||
if (targetCenter != Center)
|
||||
if (!internalPropertyChange)
|
||||
{
|
||||
if (centerAnimation != null)
|
||||
CoerceCenterProperty(TargetCenterProperty, ref targetCenter);
|
||||
|
||||
if (targetCenter != Center)
|
||||
{
|
||||
centerAnimation.Completed -= CenterAnimationCompleted;
|
||||
if (centerAnimation != null)
|
||||
{
|
||||
centerAnimation.Completed -= CenterAnimationCompleted;
|
||||
}
|
||||
|
||||
// animate private CenterPoint property by PointAnimation
|
||||
centerAnimation = new PointAnimation
|
||||
{
|
||||
From = new Point(Center.Longitude, Center.Latitude),
|
||||
To = new Point(targetCenter.Longitude, targetCenter.Latitude),
|
||||
Duration = AnimationDuration,
|
||||
FillBehavior = AnimationFillBehavior,
|
||||
EasingFunction = AnimationEasingFunction
|
||||
};
|
||||
|
||||
centerAnimation.Completed += CenterAnimationCompleted;
|
||||
this.BeginAnimation(CenterPointProperty, centerAnimation);
|
||||
}
|
||||
|
||||
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)
|
||||
private void CenterAnimationCompleted(object sender, object e)
|
||||
{
|
||||
Center = TargetCenter;
|
||||
centerAnimation.Completed -= CenterAnimationCompleted;
|
||||
centerAnimation = null;
|
||||
if (centerAnimation != null)
|
||||
{
|
||||
centerAnimation.Completed -= CenterAnimationCompleted;
|
||||
centerAnimation = null;
|
||||
|
||||
SetProperty(CenterProperty, TargetCenter);
|
||||
SetProperty(CenterPointProperty, new Point(TargetCenter.Longitude, TargetCenter.Latitude));
|
||||
ResetTransformOrigin();
|
||||
UpdateTransform();
|
||||
}
|
||||
}
|
||||
|
||||
private Location CoerceCenterProperty(Location location)
|
||||
private void CenterPointPropertyChanged(Point centerPoint)
|
||||
{
|
||||
location.Latitude = Math.Min(Math.Max(location.Latitude, -MapTransform.MaxLatitude), MapTransform.MaxLatitude);
|
||||
location.Longitude = Location.NormalizeLongitude(location.Longitude);
|
||||
return location;
|
||||
if (!internalPropertyChange)
|
||||
{
|
||||
SetProperty(CenterProperty, new Location(centerPoint.Y, centerPoint.X));
|
||||
ResetTransformOrigin();
|
||||
UpdateTransform();
|
||||
}
|
||||
}
|
||||
|
||||
private void MinZoomLevelPropertyChanged(double minZoomLevel)
|
||||
{
|
||||
var coercedValue = Math.Min(Math.Max(minZoomLevel, 0d), MaxZoomLevel);
|
||||
|
||||
if (coercedValue != minZoomLevel)
|
||||
{
|
||||
SetProperty(MinZoomLevelProperty, coercedValue);
|
||||
}
|
||||
else if (ZoomLevel < minZoomLevel)
|
||||
{
|
||||
ZoomLevel = minZoomLevel;
|
||||
}
|
||||
}
|
||||
|
||||
private void MaxZoomLevelPropertyChanged(double maxZoomLevel)
|
||||
{
|
||||
var coercedValue = Math.Min(Math.Max(maxZoomLevel, MinZoomLevel), 20d);
|
||||
|
||||
if (coercedValue != maxZoomLevel)
|
||||
{
|
||||
SetProperty(MaxZoomLevelProperty, coercedValue);
|
||||
}
|
||||
else if (ZoomLevel > maxZoomLevel)
|
||||
{
|
||||
ZoomLevel = maxZoomLevel;
|
||||
}
|
||||
}
|
||||
|
||||
private double CoerceZoomLevel(double value)
|
||||
{
|
||||
return Math.Min(Math.Max(value, MinZoomLevel), MaxZoomLevel);
|
||||
}
|
||||
|
||||
private bool CoerceZoomLevelProperty(DependencyProperty property, ref double value)
|
||||
{
|
||||
var coercedValue = CoerceZoomLevel(value);
|
||||
|
||||
if (coercedValue != value)
|
||||
{
|
||||
SetProperty(property, coercedValue);
|
||||
}
|
||||
|
||||
return coercedValue != value;
|
||||
}
|
||||
|
||||
private void ZoomLevelPropertyChanged(double zoomLevel)
|
||||
{
|
||||
if (updateTransform)
|
||||
if (!internalPropertyChange &&
|
||||
!CoerceZoomLevelProperty(ZoomLevelProperty, ref zoomLevel))
|
||||
{
|
||||
UpdateTransform();
|
||||
}
|
||||
|
||||
if (zoomLevelAnimation == null)
|
||||
{
|
||||
TargetZoomLevel = zoomLevel;
|
||||
if (zoomLevelAnimation == null)
|
||||
{
|
||||
SetProperty(TargetZoomLevelProperty, zoomLevel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void TargetZoomLevelPropertyChanged(double targetZoomLevel)
|
||||
{
|
||||
if (targetZoomLevel != ZoomLevel)
|
||||
if (!internalPropertyChange &&
|
||||
!CoerceZoomLevelProperty(TargetZoomLevelProperty, ref targetZoomLevel) &&
|
||||
targetZoomLevel != ZoomLevel)
|
||||
{
|
||||
if (zoomLevelAnimation != null)
|
||||
{
|
||||
|
|
@ -661,88 +707,109 @@ namespace MapControl
|
|||
|
||||
zoomLevelAnimation = new DoubleAnimation
|
||||
{
|
||||
From = ZoomLevel,
|
||||
To = targetZoomLevel,
|
||||
Duration = TimeSpan.FromSeconds(0.5),
|
||||
FillBehavior = FillBehavior.Stop,
|
||||
EasingFunction = new QuadraticEase { EasingMode = EasingMode.EaseOut }
|
||||
Duration = AnimationDuration,
|
||||
FillBehavior = AnimationFillBehavior,
|
||||
EasingFunction = AnimationEasingFunction
|
||||
};
|
||||
|
||||
zoomLevelAnimation.Completed += ZoomLevelAnimationCompleted;
|
||||
BeginAnimation(ZoomLevelProperty, zoomLevelAnimation);
|
||||
this.BeginAnimation(ZoomLevelProperty, zoomLevelAnimation);
|
||||
}
|
||||
}
|
||||
|
||||
private void ZoomLevelAnimationCompleted(object sender, EventArgs e)
|
||||
private void ZoomLevelAnimationCompleted(object sender, object e)
|
||||
{
|
||||
ZoomLevel = TargetZoomLevel;
|
||||
zoomLevelAnimation.Completed -= ZoomLevelAnimationCompleted;
|
||||
zoomLevelAnimation = null;
|
||||
ResetTransformOrigin();
|
||||
if (zoomLevelAnimation != null)
|
||||
{
|
||||
zoomLevelAnimation.Completed -= ZoomLevelAnimationCompleted;
|
||||
zoomLevelAnimation = null;
|
||||
|
||||
SetProperty(ZoomLevelProperty, TargetZoomLevel);
|
||||
UpdateTransform();
|
||||
ResetTransformOrigin();
|
||||
}
|
||||
}
|
||||
|
||||
private double CoerceZoomLevelProperty(double zoomLevel)
|
||||
private double CoerceHeading(double value)
|
||||
{
|
||||
return Math.Min(Math.Max(zoomLevel, MinZoomLevel), MaxZoomLevel);
|
||||
return (value >= -180d && value <= 360d) ? value : ((value + 360d) % 360d);
|
||||
}
|
||||
|
||||
private bool CoerceHeadingProperty(DependencyProperty property, ref double value)
|
||||
{
|
||||
var coercedValue = CoerceHeading(value);
|
||||
|
||||
if (coercedValue != value)
|
||||
{
|
||||
SetProperty(property, coercedValue);
|
||||
}
|
||||
|
||||
return coercedValue != value;
|
||||
}
|
||||
|
||||
private void HeadingPropertyChanged(double heading)
|
||||
{
|
||||
if (updateTransform)
|
||||
if (!internalPropertyChange)
|
||||
{
|
||||
CoerceHeadingProperty(HeadingProperty, ref heading);
|
||||
UpdateTransform();
|
||||
}
|
||||
|
||||
if (headingAnimation == null)
|
||||
{
|
||||
TargetHeading = heading;
|
||||
if (headingAnimation == null)
|
||||
{
|
||||
SetProperty(TargetHeadingProperty, heading);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void TargetHeadingPropertyChanged(double targetHeading)
|
||||
{
|
||||
if (targetHeading != Heading)
|
||||
if (!internalPropertyChange)
|
||||
{
|
||||
if (headingAnimation != null)
|
||||
CoerceHeadingProperty(TargetHeadingProperty, ref targetHeading);
|
||||
|
||||
if (targetHeading != Heading)
|
||||
{
|
||||
headingAnimation.Completed -= HeadingAnimationCompleted;
|
||||
var delta = targetHeading - Heading;
|
||||
|
||||
if (delta > 180d)
|
||||
{
|
||||
delta -= 360d;
|
||||
}
|
||||
else if (delta < -180d)
|
||||
{
|
||||
delta += 360d;
|
||||
}
|
||||
|
||||
if (headingAnimation != null)
|
||||
{
|
||||
headingAnimation.Completed -= HeadingAnimationCompleted;
|
||||
}
|
||||
|
||||
headingAnimation = new DoubleAnimation
|
||||
{
|
||||
By = delta,
|
||||
Duration = AnimationDuration,
|
||||
FillBehavior = AnimationFillBehavior,
|
||||
EasingFunction = AnimationEasingFunction
|
||||
};
|
||||
|
||||
headingAnimation.Completed += HeadingAnimationCompleted;
|
||||
this.BeginAnimation(HeadingProperty, headingAnimation);
|
||||
}
|
||||
|
||||
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)
|
||||
private void HeadingAnimationCompleted(object sender, object e)
|
||||
{
|
||||
Heading = TargetHeading;
|
||||
headingAnimation.Completed -= HeadingAnimationCompleted;
|
||||
headingAnimation = null;
|
||||
}
|
||||
if (headingAnimation != null)
|
||||
{
|
||||
headingAnimation.Completed -= HeadingAnimationCompleted;
|
||||
headingAnimation = null;
|
||||
|
||||
private double CoerceHeadingProperty(double heading)
|
||||
{
|
||||
return ((heading % 360d) + 360d) % 360d;
|
||||
SetProperty(HeadingProperty, TargetHeading);
|
||||
UpdateTransform();
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateTransform()
|
||||
|
|
@ -751,23 +818,17 @@ namespace MapControl
|
|||
|
||||
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;
|
||||
scale = tileContainer.SetViewportTransform(ZoomLevel, Heading, mapTransform.Transform(transformOrigin), viewportOrigin, RenderSize);
|
||||
SetProperty(CenterProperty, ViewportPointToLocation(new Point(RenderSize.Width / 2d, RenderSize.Height / 2d)));
|
||||
}
|
||||
else
|
||||
{
|
||||
scale = tileContainer.SetViewportTransform(ZoomLevel, Heading, MapTransform.Transform(Center), viewportOrigin, RenderSize);
|
||||
scale = tileContainer.SetViewportTransform(ZoomLevel, Heading, mapTransform.Transform(Center), viewportOrigin, RenderSize);
|
||||
}
|
||||
|
||||
scale *= MapTransform.RelativeScale(Center) / MeterPerDegree; // Pixels per meter at center latitude
|
||||
|
||||
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;
|
||||
SetTransformMatrixes(scale);
|
||||
|
||||
OnViewportChanged();
|
||||
}
|
||||
|
|
|
|||
132
MapControl/MapControl.Silverlight.csproj
Normal file
132
MapControl/MapControl.Silverlight.csproj
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProductVersion>8.0.50727</ProductVersion>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<ProjectGuid>{EB133B78-DEFF-416A-8F0C-89E54D766576}</ProjectGuid>
|
||||
<ProjectTypeGuids>{A1591282-1198-4647-A2B1-27E5FF5F6F3B};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>MapControl</RootNamespace>
|
||||
<AssemblyName>MapControl.Silverlight</AssemblyName>
|
||||
<TargetFrameworkIdentifier>Silverlight</TargetFrameworkIdentifier>
|
||||
<TargetFrameworkVersion>v5.0</TargetFrameworkVersion>
|
||||
<SilverlightVersion>$(TargetFrameworkVersion)</SilverlightVersion>
|
||||
<SilverlightApplication>false</SilverlightApplication>
|
||||
<ValidateXaml>true</ValidateXaml>
|
||||
<ThrowErrorsInValidation>true</ThrowErrorsInValidation>
|
||||
<TargetFrameworkProfile />
|
||||
</PropertyGroup>
|
||||
<!-- This property group is only here to support building this project using the
|
||||
MSBuild 3.5 toolset. In order to work correctly with this older toolset, it needs
|
||||
to set the TargetFrameworkVersion to v3.5 -->
|
||||
<PropertyGroup Condition="'$(MSBuildToolsVersion)' == '3.5'">
|
||||
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE;SILVERLIGHT</DefineConstants>
|
||||
<NoStdLib>true</NoStdLib>
|
||||
<NoConfig>true</NoConfig>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>none</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE;SILVERLIGHT</DefineConstants>
|
||||
<NoStdLib>true</NoStdLib>
|
||||
<NoConfig>true</NoConfig>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<RunPostBuildEvent>OnBuildSuccess</RunPostBuildEvent>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="mscorlib" />
|
||||
<Reference Include="System.Windows" />
|
||||
<Reference Include="system" />
|
||||
<Reference Include="System.Core">
|
||||
<HintPath>$(TargetFrameworkDirectory)System.Core.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="System.Net" />
|
||||
<Reference Include="System.Windows.Browser" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="AnimationEx.Silverlight.cs" />
|
||||
<Compile Include="Int32Rect.cs" />
|
||||
<Compile Include="Location.cs" />
|
||||
<Compile Include="LocationCollection.cs" />
|
||||
<Compile Include="LocationCollectionConverter.cs" />
|
||||
<Compile Include="LocationConverter.cs" />
|
||||
<Compile Include="Map.cs" />
|
||||
<Compile Include="Map.Silverlight.WPF.cs" />
|
||||
<Compile Include="MapBase.cs" />
|
||||
<Compile Include="MapBase.Silverlight.WinRT.cs" />
|
||||
<Compile Include="MapGraticule.cs" />
|
||||
<Compile Include="MapGraticule.Silverlight.WinRT.cs" />
|
||||
<Compile Include="MapItem.cs" />
|
||||
<Compile Include="MapItemsControl.cs" />
|
||||
<Compile Include="MapOverlay.cs" />
|
||||
<Compile Include="MapOverlay.Silverlight.WinRT.cs" />
|
||||
<Compile Include="MapPanel.cs" />
|
||||
<Compile Include="MapPolygon.cs" />
|
||||
<Compile Include="MapPolyline.cs" />
|
||||
<Compile Include="MapPolyline.Silverlight.cs" />
|
||||
<Compile Include="MapTransform.cs" />
|
||||
<Compile Include="MatrixEx.cs" />
|
||||
<Compile Include="MercatorTransform.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Pushpin.cs" />
|
||||
<Compile Include="Tile.cs" />
|
||||
<Compile Include="Tile.Silverlight.WinRT.cs" />
|
||||
<Compile Include="TileContainer.cs" />
|
||||
<Compile Include="TileContainer.Silverlight.WinRT.cs" />
|
||||
<Compile Include="TileImageLoader.Silverlight.WinRT.cs" />
|
||||
<Compile Include="TileLayer.cs" />
|
||||
<Compile Include="TileLayer.Silverlight.WinRT.cs" />
|
||||
<Compile Include="TileLayerCollection.cs" />
|
||||
<Compile Include="TileSource.cs" />
|
||||
<Compile Include="TileSourceConverter.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup />
|
||||
<ItemGroup>
|
||||
<Page Include="Themes\Generic.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\Silverlight\$(SilverlightVersion)\Microsoft.Silverlight.CSharp.targets" />
|
||||
<ProjectExtensions>
|
||||
<VisualStudio>
|
||||
<FlavorProperties GUID="{A1591282-1198-4647-A2B1-27E5FF5F6F3B}">
|
||||
<SilverlightProjectProperties />
|
||||
</FlavorProperties>
|
||||
</VisualStudio>
|
||||
</ProjectExtensions>
|
||||
<PropertyGroup>
|
||||
<PreBuildEvent>
|
||||
</PreBuildEvent>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<PostBuildEvent>
|
||||
</PostBuildEvent>
|
||||
</PropertyGroup>
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
||||
29
MapControl/MapControl.Silverlight.csproj.user
Normal file
29
MapControl/MapControl.Silverlight.csproj.user
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ProjectExtensions>
|
||||
<VisualStudio>
|
||||
<FlavorProperties GUID="{A1591282-1198-4647-A2B1-27E5FF5F6F3B}">
|
||||
<SilverlightProjectProperties>
|
||||
<StartPageUrl>
|
||||
</StartPageUrl>
|
||||
<StartAction>DynamicPage</StartAction>
|
||||
<AspNetDebugging>True</AspNetDebugging>
|
||||
<NativeDebugging>False</NativeDebugging>
|
||||
<SQLDebugging>False</SQLDebugging>
|
||||
<ExternalProgram>
|
||||
</ExternalProgram>
|
||||
<StartExternalURL>
|
||||
</StartExternalURL>
|
||||
<StartCmdLineArguments>
|
||||
</StartCmdLineArguments>
|
||||
<StartWorkingDirectory>
|
||||
</StartWorkingDirectory>
|
||||
<ShowWebRefOnDebugPrompt>True</ShowWebRefOnDebugPrompt>
|
||||
<OutOfBrowserProjectToDebug>
|
||||
</OutOfBrowserProjectToDebug>
|
||||
<ShowRiaSvcsOnDebugPrompt>True</ShowRiaSvcsOnDebugPrompt>
|
||||
</SilverlightProjectProperties>
|
||||
</FlavorProperties>
|
||||
</VisualStudio>
|
||||
</ProjectExtensions>
|
||||
</Project>
|
||||
|
|
@ -1,19 +1,17 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProductVersion>8.0.30703</ProductVersion>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<ProjectGuid>{06481252-2310-414A-B9FC-D5739FDF6BD3}</ProjectGuid>
|
||||
<ProjectGuid>{226F3575-B683-446D-A2F0-181291DC8787}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>MapControl</RootNamespace>
|
||||
<AssemblyName>MapControl</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
|
||||
<AssemblyName>MapControl.WPF</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<TargetFrameworkProfile>
|
||||
</TargetFrameworkProfile>
|
||||
<TargetFrameworkProfile />
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
|
|
@ -23,17 +21,19 @@
|
|||
<DefineConstants>TRACE;DEBUG</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>none</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>
|
||||
</DefineConstants>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<RunPostBuildEvent>OnBuildSuccess</RunPostBuildEvent>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="PresentationCore" />
|
||||
|
|
@ -45,40 +45,62 @@
|
|||
<Reference Include="WindowsBase" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="GlyphRunText.cs" />
|
||||
<Compile Include="ImageTileSource.cs" />
|
||||
<Compile Include="MapGraticule.WPF.cs" />
|
||||
<Compile Include="MapOverlay.WPF.cs" />
|
||||
<Compile Include="Location.cs" />
|
||||
<Compile Include="LocationAnimation.cs" />
|
||||
<Compile Include="LocationCollection.cs" />
|
||||
<Compile Include="MapElement.cs" />
|
||||
<Compile Include="MapGraticule.cs" />
|
||||
<Compile Include="MapItem.cs" />
|
||||
<Compile Include="MapItemsControl.cs" />
|
||||
<Compile Include="MapPanel.cs" />
|
||||
<Compile Include="MapPolygon.cs" />
|
||||
<Compile Include="MapPolyline.cs" />
|
||||
<Compile Include="MapOverlay.cs" />
|
||||
<Compile Include="MapScale.cs" />
|
||||
<Compile Include="Pushpin.cs" />
|
||||
<Compile Include="MapTransform.cs" />
|
||||
<Compile Include="MapBase.cs" />
|
||||
<Compile Include="LocationCollectionConverter.cs" />
|
||||
<Compile Include="LocationConverter.cs" />
|
||||
<Compile Include="Map.cs" />
|
||||
<Compile Include="Map.Silverlight.WPF.cs" />
|
||||
<Compile Include="MapBase.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="MapBase.WPF.cs" />
|
||||
<Compile Include="MapItem.cs" />
|
||||
<Compile Include="MapItem.WPF.cs" />
|
||||
<Compile Include="MapItemsControl.cs" />
|
||||
<Compile Include="MapItemsControl.WPF.cs" />
|
||||
<Compile Include="MapPanel.cs" />
|
||||
<Compile Include="MapPolyline.cs" />
|
||||
<Compile Include="MapPolyline.WPF.cs" />
|
||||
<Compile Include="MapTransform.cs" />
|
||||
<Compile Include="MercatorTransform.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Pushpin.cs" />
|
||||
<Compile Include="Pushpin.WPF.cs" />
|
||||
<Compile Include="Tile.cs" />
|
||||
<Compile Include="Tile.WPF.cs" />
|
||||
<Compile Include="TileContainer.cs" />
|
||||
<Compile Include="TileImageLoader.cs" />
|
||||
<Compile Include="TileContainer.WPF.cs" />
|
||||
<Compile Include="TileLayer.cs" />
|
||||
<Compile Include="TileLayer.WPF.cs" />
|
||||
<Compile Include="TileLayerCollection.cs" />
|
||||
<Compile Include="TileSource.cs" />
|
||||
<Compile Include="TileSourceConverter.cs" />
|
||||
<Compile Include="GlyphRunText.cs" />
|
||||
<Compile Include="ImageTileSource.cs" />
|
||||
<Compile Include="MapGraticule.cs" />
|
||||
<Compile Include="MapOverlay.cs" />
|
||||
<Compile Include="MapScale.cs" />
|
||||
<Compile Include="TileImageLoader.WPF.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Include="Themes\Generic.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup />
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<PropertyGroup>
|
||||
<PreBuildEvent>
|
||||
</PreBuildEvent>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<PostBuildEvent>
|
||||
</PostBuildEvent>
|
||||
</PropertyGroup>
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
|
|
@ -1,55 +0,0 @@
|
|||
// WPF MapControl - http://wpfmapcontrol.codeplex.com/
|
||||
// Copyright © 2012 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using System;
|
||||
using System.Windows;
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class for child elements of a MapPanel.
|
||||
/// </summary>
|
||||
public abstract class MapElement : FrameworkElement
|
||||
{
|
||||
static MapElement()
|
||||
{
|
||||
MapPanel.ParentMapPropertyKey.OverrideMetadata(
|
||||
typeof(MapElement),
|
||||
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.Inherits, ParentMapPropertyChanged));
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
MapBase oldParentMap = e.OldValue as MapBase;
|
||||
MapBase newParentMap = e.NewValue as MapBase;
|
||||
|
||||
if (oldParentMap != null)
|
||||
{
|
||||
oldParentMap.ViewportChanged -= mapElement.OnViewportChanged;
|
||||
}
|
||||
|
||||
if (newParentMap != null)
|
||||
{
|
||||
newParentMap.ViewportChanged += mapElement.OnViewportChanged;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
172
MapControl/MapGraticule.Silverlight.WinRT.cs
Normal file
172
MapControl/MapGraticule.Silverlight.WinRT.cs
Normal file
|
|
@ -0,0 +1,172 @@
|
|||
// XAML Map Control - http://xamlmapcontrol.codeplex.com/
|
||||
// Copyright © 2012 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
#if WINRT
|
||||
using Windows.Foundation;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Media;
|
||||
#else
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media;
|
||||
#endif
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
public partial class MapGraticule
|
||||
{
|
||||
private Location graticuleStart;
|
||||
private Location graticuleEnd;
|
||||
|
||||
protected override void OnViewportChanged()
|
||||
{
|
||||
var parentMap = MapPanel.GetParentMap(this);
|
||||
var bounds = parentMap.ViewportTransform.Inverse.TransformBounds(new Rect(0d, 0d, parentMap.RenderSize.Width, parentMap.RenderSize.Height));
|
||||
var start = parentMap.MapTransform.Transform(new Point(bounds.X, bounds.Y));
|
||||
var end = parentMap.MapTransform.Transform(new Point(bounds.X + bounds.Width, bounds.Y + bounds.Height));
|
||||
var minSpacing = MinLineSpacing * 360d / (Math.Pow(2d, parentMap.ZoomLevel) * 256d);
|
||||
var spacing = LineSpacings[LineSpacings.Length - 1];
|
||||
|
||||
if (spacing >= minSpacing)
|
||||
{
|
||||
spacing = LineSpacings.FirstOrDefault(s => s >= minSpacing);
|
||||
}
|
||||
|
||||
var labelsStart = new Location(
|
||||
Math.Ceiling(start.Latitude / spacing) * spacing,
|
||||
Math.Ceiling(start.Longitude / spacing) * spacing);
|
||||
|
||||
var labelsEnd = new Location(
|
||||
Math.Floor(end.Latitude / spacing) * spacing,
|
||||
Math.Floor(end.Longitude / spacing) * spacing);
|
||||
|
||||
var linesStart = new Location(
|
||||
Math.Min(Math.Max(labelsStart.Latitude - spacing, -parentMap.MapTransform.MaxLatitude), parentMap.MapTransform.MaxLatitude),
|
||||
labelsStart.Longitude - spacing);
|
||||
|
||||
var linesEnd = new Location(
|
||||
Math.Min(Math.Max(labelsEnd.Latitude + spacing, -parentMap.MapTransform.MaxLatitude), parentMap.MapTransform.MaxLatitude),
|
||||
labelsEnd.Longitude + spacing);
|
||||
|
||||
if (!linesStart.Equals(graticuleStart) || !linesEnd.Equals(graticuleEnd))
|
||||
{
|
||||
graticuleStart = linesStart;
|
||||
graticuleEnd = linesEnd;
|
||||
|
||||
Geometry.Figures.Clear();
|
||||
Geometry.Transform = parentMap.ViewportTransform;
|
||||
|
||||
for (var lat = labelsStart.Latitude; lat <= end.Latitude; lat += spacing)
|
||||
{
|
||||
var figure = new PathFigure
|
||||
{
|
||||
StartPoint = parentMap.MapTransform.Transform(new Location(lat, linesStart.Longitude)),
|
||||
IsClosed = false,
|
||||
IsFilled = false
|
||||
};
|
||||
|
||||
figure.Segments.Add(new LineSegment
|
||||
{
|
||||
Point = parentMap.MapTransform.Transform(new Location(lat, linesEnd.Longitude)),
|
||||
});
|
||||
|
||||
Geometry.Figures.Add(figure);
|
||||
}
|
||||
|
||||
for (var lon = labelsStart.Longitude; lon <= end.Longitude; lon += spacing)
|
||||
{
|
||||
var figure = new PathFigure
|
||||
{
|
||||
StartPoint = parentMap.MapTransform.Transform(new Location(linesStart.Latitude, lon)),
|
||||
IsClosed = false,
|
||||
IsFilled = false
|
||||
};
|
||||
|
||||
figure.Segments.Add(new LineSegment
|
||||
{
|
||||
Point = parentMap.MapTransform.Transform(new Location(linesEnd.Latitude, lon)),
|
||||
});
|
||||
|
||||
Geometry.Figures.Add(figure);
|
||||
}
|
||||
|
||||
var childIndex = 1; // 0 for Path
|
||||
|
||||
if (Foreground != null)
|
||||
{
|
||||
var format = spacing < 1d ? "{0} {1}°{2:00}'" : "{0} {1}°";
|
||||
var measureSize = new Size(double.PositiveInfinity, double.PositiveInfinity);
|
||||
|
||||
for (var lat = labelsStart.Latitude; lat <= end.Latitude; lat += spacing)
|
||||
{
|
||||
for (var lon = labelsStart.Longitude; lon <= end.Longitude; lon += spacing)
|
||||
{
|
||||
var location = new Location(lat, lon);
|
||||
TextBlock label;
|
||||
|
||||
if (childIndex < Children.Count)
|
||||
{
|
||||
label = (TextBlock)Children[childIndex];
|
||||
}
|
||||
else
|
||||
{
|
||||
label = new TextBlock { RenderTransform = new TransformGroup() };
|
||||
Children.Add(label);
|
||||
}
|
||||
|
||||
childIndex++;
|
||||
|
||||
if (FontFamily != null)
|
||||
{
|
||||
label.FontFamily = FontFamily;
|
||||
}
|
||||
|
||||
label.FontSize = FontSize;
|
||||
label.FontStyle = FontStyle;
|
||||
label.FontWeight = FontWeight;
|
||||
label.FontStretch = FontStretch;
|
||||
label.Foreground = Foreground;
|
||||
|
||||
label.Text = string.Format("{0}\n{1}",
|
||||
CoordinateString(lat, format, "NS"),
|
||||
CoordinateString(Location.NormalizeLongitude(lon), format, "EW"));
|
||||
|
||||
label.Measure(measureSize);
|
||||
|
||||
var translateTransform = new TranslateTransform
|
||||
{
|
||||
X = StrokeThickness / 2d + 2d,
|
||||
Y = -label.DesiredSize.Height / 2d
|
||||
};
|
||||
|
||||
var transform = (TransformGroup)label.RenderTransform;
|
||||
|
||||
if (transform.Children.Count == 0)
|
||||
{
|
||||
transform.Children.Add(translateTransform);
|
||||
transform.Children.Add(parentMap.RotateTransform);
|
||||
}
|
||||
else
|
||||
{
|
||||
transform.Children[0] = translateTransform;
|
||||
transform.Children[1] = parentMap.RotateTransform;
|
||||
}
|
||||
|
||||
MapPanel.SetLocation(label, location);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while (Children.Count > childIndex)
|
||||
{
|
||||
Children.RemoveAt(Children.Count - 1);
|
||||
}
|
||||
}
|
||||
|
||||
base.OnViewportChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
74
MapControl/MapGraticule.WPF.cs
Normal file
74
MapControl/MapGraticule.WPF.cs
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
// XAML Map Control - http://xamlmapcontrol.codeplex.com/
|
||||
// Copyright © 2012 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
public partial class MapGraticule
|
||||
{
|
||||
protected override void OnRender(DrawingContext drawingContext)
|
||||
{
|
||||
var parentMap = MapPanel.GetParentMap(this);
|
||||
|
||||
if (parentMap != null)
|
||||
{
|
||||
var bounds = parentMap.ViewportTransform.Inverse.TransformBounds(new Rect(parentMap.RenderSize));
|
||||
var start = parentMap.MapTransform.Transform(new Point(bounds.X, bounds.Y));
|
||||
var end = parentMap.MapTransform.Transform(new Point(bounds.X + bounds.Width, bounds.Y + bounds.Height));
|
||||
var minSpacing = MinLineSpacing * 360d / (Math.Pow(2d, parentMap.ZoomLevel) * 256d);
|
||||
var spacing = LineSpacings[LineSpacings.Length - 1];
|
||||
|
||||
if (spacing >= minSpacing)
|
||||
{
|
||||
spacing = LineSpacings.FirstOrDefault(s => s >= minSpacing);
|
||||
}
|
||||
|
||||
var labelsStart = new Location(
|
||||
Math.Ceiling(start.Latitude / spacing) * spacing,
|
||||
Math.Ceiling(start.Longitude / spacing) * spacing);
|
||||
|
||||
for (var lat = labelsStart.Latitude; lat <= end.Latitude; lat += spacing)
|
||||
{
|
||||
drawingContext.DrawLine(Pen,
|
||||
parentMap.LocationToViewportPoint(new Location(lat, start.Longitude)),
|
||||
parentMap.LocationToViewportPoint(new Location(lat, end.Longitude)));
|
||||
}
|
||||
|
||||
for (var lon = labelsStart.Longitude; lon <= end.Longitude; lon += spacing)
|
||||
{
|
||||
drawingContext.DrawLine(Pen,
|
||||
parentMap.LocationToViewportPoint(new Location(start.Latitude, lon)),
|
||||
parentMap.LocationToViewportPoint(new Location(end.Latitude, lon)));
|
||||
}
|
||||
|
||||
if (Foreground != null && Foreground != Brushes.Transparent)
|
||||
{
|
||||
var format = spacing < 1d ? "{0} {1}°{2:00}'" : "{0} {1}°";
|
||||
|
||||
for (var lat = labelsStart.Latitude; lat <= end.Latitude; lat += spacing)
|
||||
{
|
||||
for (var lon = labelsStart.Longitude; lon <= end.Longitude; lon += spacing)
|
||||
{
|
||||
var t = StrokeThickness / 2d;
|
||||
var p = parentMap.LocationToViewportPoint(new Location(lat, lon));
|
||||
var latPos = new Point(p.X + t + 2d, p.Y - t - FontSize / 4d);
|
||||
var lonPos = new Point(p.X + t + 2d, p.Y + t + FontSize);
|
||||
var latLabel = CoordinateString(lat, format, "NS");
|
||||
var lonLabel = CoordinateString(Location.NormalizeLongitude(lon), format, "EW");
|
||||
|
||||
drawingContext.PushTransform(new RotateTransform(parentMap.Heading, p.X, p.Y));
|
||||
drawingContext.DrawGlyphRun(Foreground, GlyphRunText.Create(latLabel, Typeface, FontSize, latPos));
|
||||
drawingContext.DrawGlyphRun(Foreground, GlyphRunText.Create(lonLabel, Typeface, FontSize, lonPos));
|
||||
drawingContext.Pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,28 +1,34 @@
|
|||
// WPF MapControl - http://wpfmapcontrol.codeplex.com/
|
||||
// XAML Map Control - http://xamlmapcontrol.codeplex.com/
|
||||
// Copyright © 2012 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
#if WINRT
|
||||
using Windows.UI.Xaml;
|
||||
#else
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
#endif
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
/// <summary>
|
||||
/// Draws a graticule overlay.
|
||||
/// </summary>
|
||||
public class MapGraticule : MapOverlay
|
||||
public partial class MapGraticule : MapOverlay
|
||||
{
|
||||
public static readonly DependencyProperty MinLineSpacingProperty = DependencyProperty.Register(
|
||||
"MinLineSpacing", typeof(double), typeof(MapGraticule), new FrameworkPropertyMetadata(100d));
|
||||
|
||||
/// <summary>
|
||||
/// Graticule line spacings in degrees.
|
||||
/// </summary>
|
||||
public static double[] LineSpacings =
|
||||
new double[] { 1d / 60d, 1d / 30d, 1d / 12d, 1d / 6d, 1d / 4d, 1d / 3d, 1d / 2d, 1d, 2d, 5d, 10d, 15d, 20d, 30d, 45d };
|
||||
|
||||
public static readonly DependencyProperty MinLineSpacingProperty = DependencyProperty.Register(
|
||||
"MinLineSpacing", typeof(double), typeof(MapGraticule), new PropertyMetadata(150d));
|
||||
|
||||
public MapGraticule()
|
||||
{
|
||||
StrokeThickness = 0.5;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Minimum spacing in pixels between adjacent graticule lines.
|
||||
/// </summary>
|
||||
|
|
@ -32,64 +38,9 @@ namespace MapControl
|
|||
set { SetValue(MinLineSpacingProperty, value); }
|
||||
}
|
||||
|
||||
protected override void OnRender(DrawingContext drawingContext)
|
||||
{
|
||||
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);
|
||||
double minSpacing = MinLineSpacing * 360d / (Math.Pow(2d, parentMap.ZoomLevel) * 256d);
|
||||
double spacing = LineSpacings[LineSpacings.Length - 1];
|
||||
|
||||
if (spacing >= minSpacing)
|
||||
{
|
||||
spacing = LineSpacings.FirstOrDefault(s => s >= minSpacing);
|
||||
}
|
||||
|
||||
double latitudeStart = Math.Ceiling(loc1.Latitude / spacing) * spacing;
|
||||
double longitudeStart = Math.Ceiling(loc1.Longitude / spacing) * spacing;
|
||||
|
||||
for (double lat = latitudeStart; lat <= loc2.Latitude; lat += spacing)
|
||||
{
|
||||
drawingContext.DrawLine(Pen,
|
||||
parentMap.LocationToViewportPoint(new Location(lat, loc1.Longitude)),
|
||||
parentMap.LocationToViewportPoint(new Location(lat, loc2.Longitude)));
|
||||
}
|
||||
|
||||
for (double lon = longitudeStart; lon <= loc2.Longitude; lon += spacing)
|
||||
{
|
||||
drawingContext.DrawLine(Pen,
|
||||
parentMap.LocationToViewportPoint(new Location(loc1.Latitude, lon)),
|
||||
parentMap.LocationToViewportPoint(new Location(loc2.Latitude, lon)));
|
||||
}
|
||||
|
||||
if (Foreground != null && Foreground != Brushes.Transparent)
|
||||
{
|
||||
string format = spacing < 1d ? "{0} {1}°{2:00}'" : "{0} {1}°";
|
||||
|
||||
for (double lat = latitudeStart; lat <= loc2.Latitude; lat += spacing)
|
||||
{
|
||||
for (double lon = longitudeStart; lon <= loc2.Longitude; lon += spacing)
|
||||
{
|
||||
double t = StrokeThickness / 2d;
|
||||
Point p = parentMap.LocationToViewportPoint(new Location(lat, lon));
|
||||
Point latPos = new Point(p.X + t + 2d, p.Y - t - FontSize / 4d);
|
||||
Point lonPos = new Point(p.X + t + 2d, p.Y + t + FontSize);
|
||||
string latString = CoordinateString(lat, format, "NS");
|
||||
string lonString = CoordinateString(Location.NormalizeLongitude(lon), format, "EW");
|
||||
|
||||
drawingContext.PushTransform(new RotateTransform(parentMap.Heading, p.X, p.Y));
|
||||
drawingContext.DrawGlyphRun(Foreground, GlyphRunText.Create(latString, Typeface, FontSize, latPos));
|
||||
drawingContext.DrawGlyphRun(Foreground, GlyphRunText.Create(lonString, Typeface, FontSize, lonPos));
|
||||
drawingContext.Pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static string CoordinateString(double value, string format, string hemispheres)
|
||||
{
|
||||
char hemisphere = hemispheres[0];
|
||||
var hemisphere = hemispheres[0];
|
||||
|
||||
if (value < -1e-8) // ~1mm
|
||||
{
|
||||
|
|
@ -97,7 +48,7 @@ namespace MapControl
|
|||
hemisphere = hemispheres[1];
|
||||
}
|
||||
|
||||
int minutes = (int)(value * 60d + 0.5);
|
||||
var minutes = (int)(value * 60d + 0.5);
|
||||
|
||||
return string.Format(format, hemisphere, minutes / 60, (double)(minutes % 60));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,85 +0,0 @@
|
|||
// WPF MapControl - http://wpfmapcontrol.codeplex.com/
|
||||
// Copyright © 2012 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using System;
|
||||
using System.Windows;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
public partial class Map
|
||||
{
|
||||
private double mouseWheelZoom = 1d;
|
||||
private Point? mousePosition;
|
||||
|
||||
public double MouseWheelZoom
|
||||
{
|
||||
get { return mouseWheelZoom; }
|
||||
set { mouseWheelZoom = value; }
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
ZoomMap(e.GetPosition(this), Math.Ceiling(ZoomLevel - 1.5));
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
|
||||
{
|
||||
base.OnMouseLeftButtonDown(e);
|
||||
|
||||
if (e.ClickCount == 1)
|
||||
{
|
||||
mousePosition = e.GetPosition(this);
|
||||
CaptureMouse();
|
||||
}
|
||||
else if (e.ClickCount == 2)
|
||||
{
|
||||
ZoomMap(e.GetPosition(this), Math.Floor(ZoomLevel + 1.5));
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)
|
||||
{
|
||||
base.OnMouseLeftButtonUp(e);
|
||||
|
||||
if (mousePosition.HasValue)
|
||||
{
|
||||
mousePosition = null;
|
||||
ReleaseMouseCapture();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnMouseMove(MouseEventArgs e)
|
||||
{
|
||||
base.OnMouseMove(e);
|
||||
|
||||
if (mousePosition.HasValue)
|
||||
{
|
||||
Point position = e.GetPosition(this);
|
||||
TranslateMap(position - mousePosition.Value);
|
||||
mousePosition = position;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnManipulationDelta(ManipulationDeltaEventArgs e)
|
||||
{
|
||||
base.OnManipulationDelta(e);
|
||||
|
||||
ManipulationDelta d = e.DeltaManipulation;
|
||||
TransformMap(e.ManipulationOrigin, d.Translation, d.Rotation, (d.Scale.X + d.Scale.Y) / 2d);
|
||||
}
|
||||
}
|
||||
}
|
||||
45
MapControl/MapItem.WPF.cs
Normal file
45
MapControl/MapItem.WPF.cs
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
// XAML Map Control - http://xamlmapcontrol.codeplex.com/
|
||||
// Copyright © 2012 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
[TemplateVisualState(Name = "NotCurrent", GroupName = "CurrentStates")]
|
||||
[TemplateVisualState(Name = "Current", GroupName = "CurrentStates")]
|
||||
public partial class MapItem
|
||||
{
|
||||
public static readonly DependencyProperty IsCurrentProperty = MapItemsControl.IsCurrentProperty.AddOwner(
|
||||
typeof(MapItem),
|
||||
new PropertyMetadata((o, e) => ((MapItem)o).IsCurrentPropertyChanged((bool)e.NewValue)));
|
||||
|
||||
static MapItem()
|
||||
{
|
||||
FrameworkElement.DefaultStyleKeyProperty.OverrideMetadata(
|
||||
typeof(MapItem), new FrameworkPropertyMetadata(typeof(MapItem)));
|
||||
}
|
||||
|
||||
public bool IsCurrent
|
||||
{
|
||||
get { return (bool)GetValue(IsCurrentProperty); }
|
||||
}
|
||||
|
||||
private void IsCurrentPropertyChanged(bool isCurrent)
|
||||
{
|
||||
var zIndex = Panel.GetZIndex(this);
|
||||
|
||||
if (isCurrent)
|
||||
{
|
||||
Panel.SetZIndex(this, zIndex | 0x40000000);
|
||||
VisualStateManager.GoToState(this, "Current", true);
|
||||
}
|
||||
else
|
||||
{
|
||||
Panel.SetZIndex(this, zIndex & ~0x40000000);
|
||||
VisualStateManager.GoToState(this, "NotCurrent", true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,139 +1,54 @@
|
|||
// WPF MapControl - http://wpfmapcontrol.codeplex.com/
|
||||
// XAML Map Control - http://xamlmapcontrol.codeplex.com/
|
||||
// Copyright © 2012 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
#if WINRT
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Data;
|
||||
#else
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Controls.Primitives;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Data;
|
||||
#endif
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
/// <summary>
|
||||
/// Container class for an item in a MapItemsControl.
|
||||
/// </summary>
|
||||
[TemplateVisualState(Name = "Normal", GroupName = "CommonStates")]
|
||||
[TemplateVisualState(Name = "MouseOver", GroupName = "CommonStates")]
|
||||
[TemplateVisualState(Name = "Disabled", GroupName = "CommonStates")]
|
||||
[TemplateVisualState(Name = "Unselected", GroupName = "SelectionStates")]
|
||||
[TemplateVisualState(Name = "Selected", GroupName = "SelectionStates")]
|
||||
[TemplateVisualState(Name = "NotCurrent", GroupName = "CurrentStates")]
|
||||
[TemplateVisualState(Name = "Current", GroupName = "CurrentStates")]
|
||||
public class MapItem : ContentControl
|
||||
public partial class MapItem : ListBoxItem
|
||||
{
|
||||
public static readonly RoutedEvent SelectedEvent = Selector.SelectedEvent.AddOwner(typeof(MapItem));
|
||||
public static readonly RoutedEvent UnselectedEvent = Selector.UnselectedEvent.AddOwner(typeof(MapItem));
|
||||
|
||||
public static readonly DependencyProperty IsSelectedProperty = Selector.IsSelectedProperty.AddOwner(
|
||||
typeof(MapItem),
|
||||
new FrameworkPropertyMetadata((o, e) => ((MapItem)o).IsSelectedPropertyChanged((bool)e.NewValue)));
|
||||
|
||||
static MapItem()
|
||||
{
|
||||
FrameworkElement.DefaultStyleKeyProperty.OverrideMetadata(
|
||||
typeof(MapItem), new FrameworkPropertyMetadata(typeof(MapItem)));
|
||||
|
||||
MapItemsControl.IsCurrentPropertyKey.OverrideMetadata(
|
||||
typeof(MapItem),
|
||||
new FrameworkPropertyMetadata((o, e) => ((MapItem)o).IsCurrentPropertyChanged((bool)e.NewValue)));
|
||||
}
|
||||
public static readonly DependencyProperty LocationPathProperty = DependencyProperty.Register(
|
||||
"LocationPath", typeof(string), typeof(MapItem), new PropertyMetadata(null, LocationPathPropertyChanged));
|
||||
|
||||
public MapItem()
|
||||
{
|
||||
IsEnabledChanged += IsEnabledPropertyChanged;
|
||||
MapPanel.AddParentMapHandlers(this);
|
||||
DefaultStyleKey = typeof(MapItem);
|
||||
}
|
||||
|
||||
public MapBase ParentMap
|
||||
/// <summary>
|
||||
/// Gets or sets the property path that is used to bind the MapPanel.Location attached property.
|
||||
/// </summary>
|
||||
public string LocationPath
|
||||
{
|
||||
get { return MapPanel.GetParentMap(this); }
|
||||
get { return (string)GetValue(LocationPathProperty); }
|
||||
set { SetValue(LocationPathProperty, value); }
|
||||
}
|
||||
|
||||
public bool IsSelected
|
||||
private static void LocationPathPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
get { return (bool)GetValue(IsSelectedProperty); }
|
||||
set { SetValue(IsSelectedProperty, value); }
|
||||
}
|
||||
string path = e.NewValue as string;
|
||||
|
||||
public bool IsCurrent
|
||||
{
|
||||
get { return MapItemsControl.GetIsCurrent(this); }
|
||||
}
|
||||
|
||||
public event RoutedEventHandler Selected
|
||||
{
|
||||
add { AddHandler(SelectedEvent, value); }
|
||||
remove { RemoveHandler(SelectedEvent, value); }
|
||||
}
|
||||
|
||||
public event RoutedEventHandler Unselected
|
||||
{
|
||||
add { AddHandler(UnselectedEvent, value); }
|
||||
remove { RemoveHandler(UnselectedEvent, value); }
|
||||
}
|
||||
|
||||
protected override void OnMouseEnter(MouseEventArgs e)
|
||||
{
|
||||
base.OnMouseEnter(e);
|
||||
|
||||
if (IsEnabled)
|
||||
if (!string.IsNullOrWhiteSpace(path))
|
||||
{
|
||||
VisualStateManager.GoToState(this, "MouseOver", true);
|
||||
}
|
||||
}
|
||||
var binding = new Binding
|
||||
{
|
||||
Path = new PropertyPath(path)
|
||||
};
|
||||
|
||||
protected override void OnMouseLeave(MouseEventArgs e)
|
||||
{
|
||||
base.OnMouseLeave(e);
|
||||
|
||||
if (IsEnabled)
|
||||
{
|
||||
VisualStateManager.GoToState(this, "Normal", true);
|
||||
}
|
||||
}
|
||||
|
||||
private void IsEnabledPropertyChanged(object sender, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
if (!(bool)e.NewValue)
|
||||
{
|
||||
VisualStateManager.GoToState(this, "Disabled", true);
|
||||
}
|
||||
else if (IsMouseOver)
|
||||
{
|
||||
VisualStateManager.GoToState(this, "MouseOver", true);
|
||||
}
|
||||
else
|
||||
{
|
||||
VisualStateManager.GoToState(this, "Normal", true);
|
||||
}
|
||||
}
|
||||
|
||||
private void IsSelectedPropertyChanged(bool isSelected)
|
||||
{
|
||||
if (isSelected)
|
||||
{
|
||||
RaiseEvent(new RoutedEventArgs(SelectedEvent));
|
||||
VisualStateManager.GoToState(this, "Selected", true);
|
||||
}
|
||||
else
|
||||
{
|
||||
RaiseEvent(new RoutedEventArgs(UnselectedEvent));
|
||||
VisualStateManager.GoToState(this, "Unselected", true);
|
||||
}
|
||||
}
|
||||
|
||||
private void IsCurrentPropertyChanged(bool isCurrent)
|
||||
{
|
||||
int zIndex = Panel.GetZIndex(this);
|
||||
|
||||
if (isCurrent)
|
||||
{
|
||||
Panel.SetZIndex(this, zIndex | 0x40000000);
|
||||
VisualStateManager.GoToState(this, "Current", true);
|
||||
}
|
||||
else
|
||||
{
|
||||
Panel.SetZIndex(this, zIndex & ~0x40000000);
|
||||
VisualStateManager.GoToState(this, "NotCurrent", true);
|
||||
BindingOperations.SetBinding(obj, MapPanel.LocationProperty, binding);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
58
MapControl/MapItemsControl.WPF.cs
Normal file
58
MapControl/MapItemsControl.WPF.cs
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
// XAML Map Control - http://xamlmapcontrol.codeplex.com/
|
||||
// Copyright © 2012 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Windows;
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
public partial class MapItemsControl
|
||||
{
|
||||
public static readonly DependencyProperty IsCurrentProperty = DependencyProperty.RegisterAttached(
|
||||
"IsCurrent", typeof(bool), typeof(MapItemsControl), null);
|
||||
|
||||
static MapItemsControl()
|
||||
{
|
||||
FrameworkElement.DefaultStyleKeyProperty.OverrideMetadata(
|
||||
typeof(MapItemsControl), new FrameworkPropertyMetadata(typeof(MapItemsControl)));
|
||||
}
|
||||
|
||||
partial void Initialize()
|
||||
{
|
||||
Items.CurrentChanging += OnCurrentItemChanging;
|
||||
Items.CurrentChanged += OnCurrentItemChanged;
|
||||
}
|
||||
|
||||
public static bool GetIsCurrent(UIElement element)
|
||||
{
|
||||
return (bool)element.GetValue(IsCurrentProperty);
|
||||
}
|
||||
|
||||
private static void SetIsCurrent(UIElement element, bool value)
|
||||
{
|
||||
element.SetValue(IsCurrentProperty, value);
|
||||
}
|
||||
|
||||
private void OnCurrentItemChanging(object sender, CurrentChangingEventArgs e)
|
||||
{
|
||||
var container = ContainerFromItem(Items.CurrentItem);
|
||||
|
||||
if (container != null)
|
||||
{
|
||||
SetIsCurrent(container, false);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnCurrentItemChanged(object sender, EventArgs e)
|
||||
{
|
||||
var container = ContainerFromItem(Items.CurrentItem);
|
||||
|
||||
if (container != null)
|
||||
{
|
||||
SetIsCurrent(container, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,15 +1,14 @@
|
|||
// WPF MapControl - http://wpfmapcontrol.codeplex.com/
|
||||
// XAML Map Control - http://xamlmapcontrol.codeplex.com/
|
||||
// Copyright © 2012 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.ComponentModel;
|
||||
#if WINRT
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
#else
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Controls.Primitives;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
#endif
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
|
|
@ -17,54 +16,20 @@ namespace MapControl
|
|||
/// Manages a collection of selectable items on a Map. Uses MapItem as container for items
|
||||
/// and updates the IsCurrent attached property when the Items.CurrentItem property changes.
|
||||
/// </summary>
|
||||
public class MapItemsControl : MultiSelector
|
||||
public partial class MapItemsControl : ListBox
|
||||
{
|
||||
public static readonly DependencyProperty SelectionModeProperty = DependencyProperty.Register(
|
||||
"SelectionMode", typeof(SelectionMode), typeof(MapItemsControl),
|
||||
new FrameworkPropertyMetadata((o, e) => ((MapItemsControl)o).CanSelectMultipleItems = (SelectionMode)e.NewValue != SelectionMode.Single));
|
||||
|
||||
public static readonly DependencyProperty SelectionGeometryProperty = DependencyProperty.Register(
|
||||
"SelectionGeometry", typeof(Geometry), typeof(MapItemsControl),
|
||||
new FrameworkPropertyMetadata((o, e) => ((MapItemsControl)o).SelectionGeometryPropertyChanged((Geometry)e.NewValue)));
|
||||
|
||||
internal static readonly DependencyPropertyKey IsCurrentPropertyKey = DependencyProperty.RegisterAttachedReadOnly(
|
||||
"IsCurrent", typeof(bool), typeof(MapItemsControl), null);
|
||||
|
||||
public static readonly DependencyProperty IsCurrentProperty = IsCurrentPropertyKey.DependencyProperty;
|
||||
|
||||
public MapItemsControl()
|
||||
{
|
||||
Style = (Style)FindResource(typeof(MapItemsControl));
|
||||
CanSelectMultipleItems = SelectionMode != SelectionMode.Single;
|
||||
Items.CurrentChanging += OnCurrentItemChanging;
|
||||
Items.CurrentChanged += OnCurrentItemChanged;
|
||||
MapPanel.AddParentMapHandlers(this);
|
||||
DefaultStyleKey = typeof(MapItemsControl);
|
||||
Initialize();
|
||||
}
|
||||
|
||||
public MapBase ParentMap
|
||||
{
|
||||
get { return MapPanel.GetParentMap(this); }
|
||||
}
|
||||
partial void Initialize();
|
||||
|
||||
public SelectionMode SelectionMode
|
||||
protected override DependencyObject GetContainerForItemOverride()
|
||||
{
|
||||
get { return (SelectionMode)GetValue(SelectionModeProperty); }
|
||||
set { SetValue(SelectionModeProperty, value); }
|
||||
}
|
||||
|
||||
public Geometry SelectionGeometry
|
||||
{
|
||||
get { return (Geometry)GetValue(SelectionGeometryProperty); }
|
||||
set { SetValue(SelectionGeometryProperty, value); }
|
||||
}
|
||||
|
||||
public static bool GetIsCurrent(UIElement element)
|
||||
{
|
||||
return (bool)element.GetValue(IsCurrentProperty);
|
||||
}
|
||||
|
||||
public static void SetIsCurrent(UIElement element, bool value)
|
||||
{
|
||||
element.SetValue(IsCurrentPropertyKey, value);
|
||||
return new MapItem();
|
||||
}
|
||||
|
||||
public UIElement ContainerFromItem(object item)
|
||||
|
|
@ -76,182 +41,5 @@ namespace MapControl
|
|||
{
|
||||
return container != null ? ItemContainerGenerator.ItemFromContainer(container) : null;
|
||||
}
|
||||
|
||||
public IList GetItemsInGeometry(Geometry geometry)
|
||||
{
|
||||
return GetItemsInGeometry(geometry, new ArrayList());
|
||||
}
|
||||
|
||||
protected override bool IsItemItsOwnContainerOverride(object item)
|
||||
{
|
||||
return item is UIElement;
|
||||
}
|
||||
|
||||
protected override DependencyObject GetContainerForItemOverride()
|
||||
{
|
||||
return new MapItem();
|
||||
}
|
||||
|
||||
protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
|
||||
{
|
||||
base.PrepareContainerForItemOverride(element, item);
|
||||
|
||||
UIElement container = (UIElement)element;
|
||||
container.MouseLeftButtonDown += ContainerMouseLeftButtonDown;
|
||||
container.TouchDown += ContainerTouchDown;
|
||||
container.TouchUp += ContainerTouchUp;
|
||||
}
|
||||
|
||||
protected override void ClearContainerForItemOverride(DependencyObject element, object item)
|
||||
{
|
||||
base.ClearContainerForItemOverride(element, item);
|
||||
|
||||
UIElement container = (UIElement)element;
|
||||
container.MouseLeftButtonDown -= ContainerMouseLeftButtonDown;
|
||||
container.TouchDown -= ContainerTouchDown;
|
||||
container.TouchUp -= ContainerTouchUp;
|
||||
}
|
||||
|
||||
private void ContainerMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
e.Handled = true;
|
||||
UIElement container = (UIElement)sender;
|
||||
UIElement selectedContainer;
|
||||
|
||||
if (SelectionMode != SelectionMode.Extended || (Keyboard.Modifiers & ModifierKeys.Control) != 0)
|
||||
{
|
||||
Selector.SetIsSelected(container, !Selector.GetIsSelected(container));
|
||||
}
|
||||
else if ((Keyboard.Modifiers & ModifierKeys.Shift) == 0)
|
||||
{
|
||||
SelectedItem = ItemFromContainer(container);
|
||||
}
|
||||
else if ((selectedContainer = ContainerFromItem(SelectedItem)) != null)
|
||||
{
|
||||
Point? p1 = MapPanel.GetViewportPosition(selectedContainer);
|
||||
Point? p2 = MapPanel.GetViewportPosition(container);
|
||||
|
||||
if (p1.HasValue && p2.HasValue)
|
||||
{
|
||||
Rect rect = new Rect(p1.Value, p2.Value);
|
||||
|
||||
BeginUpdateSelectedItems();
|
||||
SelectedItems.Clear();
|
||||
SelectedItems.Add(SelectedItem);
|
||||
|
||||
foreach (object item in Items)
|
||||
{
|
||||
if (item != SelectedItem && IsItemInRect(item, rect))
|
||||
{
|
||||
SelectedItems.Add(item);
|
||||
}
|
||||
}
|
||||
|
||||
EndUpdateSelectedItems();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ContainerTouchDown(object sender, TouchEventArgs e)
|
||||
{
|
||||
e.Handled = true; // get TouchUp event
|
||||
}
|
||||
|
||||
private void ContainerTouchUp(object sender, TouchEventArgs e)
|
||||
{
|
||||
e.Handled = true;
|
||||
UIElement container = (UIElement)sender;
|
||||
Selector.SetIsSelected(container, !Selector.GetIsSelected(container));
|
||||
}
|
||||
|
||||
private void OnCurrentItemChanging(object sender, CurrentChangingEventArgs e)
|
||||
{
|
||||
UIElement container = ContainerFromItem(Items.CurrentItem);
|
||||
|
||||
if (container != null)
|
||||
{
|
||||
SetIsCurrent(container, false);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnCurrentItemChanged(object sender, EventArgs e)
|
||||
{
|
||||
UIElement container = ContainerFromItem(Items.CurrentItem);
|
||||
|
||||
if (container != null)
|
||||
{
|
||||
SetIsCurrent(container, true);
|
||||
}
|
||||
}
|
||||
|
||||
private void SelectionGeometryPropertyChanged(Geometry geometry)
|
||||
{
|
||||
if (geometry != null)
|
||||
{
|
||||
if (SelectionMode == SelectionMode.Single)
|
||||
{
|
||||
SelectedItem = GetFirstItemInGeometry(geometry);
|
||||
}
|
||||
else
|
||||
{
|
||||
BeginUpdateSelectedItems();
|
||||
SelectedItems.Clear();
|
||||
GetItemsInGeometry(geometry, SelectedItems);
|
||||
EndUpdateSelectedItems();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private object GetFirstItemInGeometry(Geometry geometry)
|
||||
{
|
||||
if (!geometry.IsEmpty())
|
||||
{
|
||||
foreach (object item in Items)
|
||||
{
|
||||
if (IsItemInGeometry(item, geometry))
|
||||
{
|
||||
return item;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private IList GetItemsInGeometry(Geometry geometry, IList items)
|
||||
{
|
||||
if (!geometry.IsEmpty())
|
||||
{
|
||||
foreach (object item in Items)
|
||||
{
|
||||
if (IsItemInGeometry(item, geometry))
|
||||
{
|
||||
items.Add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
private bool IsItemInGeometry(object item, Geometry geometry)
|
||||
{
|
||||
UIElement container = ContainerFromItem(item);
|
||||
Point? viewportPosition;
|
||||
|
||||
return container != null
|
||||
&& (viewportPosition = MapPanel.GetViewportPosition(container)).HasValue
|
||||
&& geometry.FillContains(viewportPosition.Value);
|
||||
}
|
||||
|
||||
private bool IsItemInRect(object item, Rect rect)
|
||||
{
|
||||
UIElement container = ContainerFromItem(item);
|
||||
Point? viewportPosition;
|
||||
|
||||
return container != null
|
||||
&& (viewportPosition = MapPanel.GetViewportPosition(container)).HasValue
|
||||
&& rect.Contains(viewportPosition.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
128
MapControl/MapOverlay.Silverlight.WinRT.cs
Normal file
128
MapControl/MapOverlay.Silverlight.WinRT.cs
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
// XAML Map Control - http://xamlmapcontrol.codeplex.com/
|
||||
// Copyright © 2012 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
#if WINRT
|
||||
using Windows.UI;
|
||||
using Windows.UI.Text;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Media;
|
||||
using Windows.UI.Xaml.Shapes;
|
||||
#else
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Shapes;
|
||||
#endif
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
public partial class MapOverlay : MapPanel
|
||||
{
|
||||
public static readonly DependencyProperty FontFamilyProperty = DependencyProperty.Register(
|
||||
"FontFamily", typeof(FontFamily), typeof(MapOverlay),
|
||||
new PropertyMetadata(default(FontFamily), (o, e) => ((MapOverlay)o).FontSizePropertyChanged()));
|
||||
|
||||
public static readonly DependencyProperty FontSizeProperty = DependencyProperty.Register(
|
||||
"FontSize", typeof(double), typeof(MapOverlay),
|
||||
new PropertyMetadata(10d, (o, e) => ((MapOverlay)o).FontSizePropertyChanged()));
|
||||
|
||||
public static readonly DependencyProperty FontStyleProperty = DependencyProperty.Register(
|
||||
"FontStyle", typeof(FontStyle), typeof(MapOverlay),
|
||||
new PropertyMetadata(default(FontStyle), (o, e) => ((MapOverlay)o).FontSizePropertyChanged()));
|
||||
|
||||
public static readonly DependencyProperty FontStretchProperty = DependencyProperty.Register(
|
||||
"FontStretch", typeof(FontStretch), typeof(MapOverlay),
|
||||
new PropertyMetadata(default(FontStretch), (o, e) => ((MapOverlay)o).FontSizePropertyChanged()));
|
||||
|
||||
public static readonly DependencyProperty FontWeightProperty = DependencyProperty.Register(
|
||||
"FontWeight", typeof(FontWeight), typeof(MapOverlay),
|
||||
new PropertyMetadata(FontWeights.Normal, (o, e) => ((MapOverlay)o).FontSizePropertyChanged()));
|
||||
|
||||
public static readonly DependencyProperty ForegroundProperty = DependencyProperty.Register(
|
||||
"Foreground", typeof(Brush), typeof(MapOverlay),
|
||||
new PropertyMetadata(new SolidColorBrush(Colors.Black), (o, e) => ((MapOverlay)o).ForegroundPropertyChanged()));
|
||||
|
||||
public static readonly DependencyProperty StrokeProperty = DependencyProperty.Register(
|
||||
"Stroke", typeof(Brush), typeof(MapOverlay),
|
||||
new PropertyMetadata(new SolidColorBrush(Colors.Black), (o, e) => ((MapOverlay)o).Path.Stroke = (Brush)e.NewValue));
|
||||
|
||||
public static readonly DependencyProperty StrokeThicknessProperty = DependencyProperty.Register(
|
||||
"StrokeThickness", typeof(double), typeof(MapOverlay),
|
||||
new PropertyMetadata(1d, (o, e) => ((MapOverlay)o).StrokeThicknessPropertyChanged((double)e.NewValue)));
|
||||
|
||||
public static readonly DependencyProperty StrokeDashArrayProperty = DependencyProperty.Register(
|
||||
"StrokeDashArray", typeof(DoubleCollection), typeof(MapOverlay),
|
||||
new PropertyMetadata(null, (o, e) => ((MapOverlay)o).Path.StrokeDashArray = (DoubleCollection)e.NewValue));
|
||||
|
||||
public static readonly DependencyProperty StrokeDashOffsetProperty = DependencyProperty.Register(
|
||||
"StrokeDashOffset", typeof(double), typeof(MapOverlay),
|
||||
new PropertyMetadata(0d, (o, e) => ((MapOverlay)o).Path.StrokeDashOffset = (double)e.NewValue));
|
||||
|
||||
public static readonly DependencyProperty StrokeDashCapProperty = DependencyProperty.Register(
|
||||
"StrokeDashCap", typeof(PenLineCap), typeof(MapOverlay),
|
||||
new PropertyMetadata(default(PenLineCap), (o, e) => ((MapOverlay)o).Path.StrokeDashCap = (PenLineCap)e.NewValue));
|
||||
|
||||
public static readonly DependencyProperty StrokeStartLineCapProperty = DependencyProperty.Register(
|
||||
"StrokeStartLineCap", typeof(PenLineCap), typeof(MapOverlay),
|
||||
new PropertyMetadata(default(PenLineCap), (o, e) => ((MapOverlay)o).Path.StrokeStartLineCap = (PenLineCap)e.NewValue));
|
||||
|
||||
public static readonly DependencyProperty StrokeEndLineCapProperty = DependencyProperty.Register(
|
||||
"StrokeEndLineCap", typeof(PenLineCap), typeof(MapOverlay),
|
||||
new PropertyMetadata(default(PenLineCap), (o, e) => ((MapOverlay)o).Path.StrokeEndLineCap = (PenLineCap)e.NewValue));
|
||||
|
||||
public static readonly DependencyProperty StrokeLineJoinProperty = DependencyProperty.Register(
|
||||
"StrokeLineJoin", typeof(PenLineJoin), typeof(MapOverlay),
|
||||
new PropertyMetadata(default(PenLineJoin), (o, e) => ((MapOverlay)o).Path.StrokeLineJoin = (PenLineJoin)e.NewValue));
|
||||
|
||||
public static readonly DependencyProperty StrokeMiterLimitProperty = DependencyProperty.Register(
|
||||
"StrokeMiterLimit", typeof(double), typeof(MapOverlay),
|
||||
new PropertyMetadata(1d, (o, e) => ((MapOverlay)o).Path.StrokeMiterLimit = (double)e.NewValue));
|
||||
|
||||
protected readonly Path Path = new Path();
|
||||
protected readonly PathGeometry Geometry = new PathGeometry();
|
||||
|
||||
partial void Initialize()
|
||||
{
|
||||
Path.Stroke = Stroke;
|
||||
Path.StrokeThickness = StrokeThickness;
|
||||
Path.StrokeDashArray = StrokeDashArray;
|
||||
Path.StrokeDashOffset = StrokeDashOffset;
|
||||
Path.StrokeDashCap = StrokeDashCap;
|
||||
Path.StrokeStartLineCap = StrokeStartLineCap;
|
||||
Path.StrokeEndLineCap = StrokeEndLineCap;
|
||||
Path.StrokeLineJoin = StrokeLineJoin;
|
||||
Path.StrokeMiterLimit = StrokeMiterLimit;
|
||||
Path.Data = Geometry;
|
||||
Children.Add(Path);
|
||||
}
|
||||
|
||||
private void FontSizePropertyChanged()
|
||||
{
|
||||
if (GetParentMap(this) != null)
|
||||
{
|
||||
// FontSize may affect layout
|
||||
OnViewportChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private void ForegroundPropertyChanged()
|
||||
{
|
||||
if (GetParentMap(this) != null)
|
||||
{
|
||||
// Foreground may affect rendering
|
||||
OnViewportChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private void StrokeThicknessPropertyChanged(double thickness)
|
||||
{
|
||||
Path.StrokeThickness = thickness;
|
||||
|
||||
if (GetParentMap(this) != null)
|
||||
{
|
||||
// StrokeThickness may affect layout
|
||||
OnViewportChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
153
MapControl/MapOverlay.WPF.cs
Normal file
153
MapControl/MapOverlay.WPF.cs
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
// XAML Map Control - http://xamlmapcontrol.codeplex.com/
|
||||
// Copyright © 2012 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using System;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Shapes;
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
public partial class MapOverlay : FrameworkElement, IMapElement
|
||||
{
|
||||
public static readonly DependencyProperty BackgroundProperty = Control.BackgroundProperty.AddOwner(
|
||||
typeof(MapOverlay));
|
||||
|
||||
public static readonly DependencyProperty FontSizeProperty = Control.FontSizeProperty.AddOwner(
|
||||
typeof(MapOverlay));
|
||||
|
||||
public static readonly DependencyProperty FontFamilyProperty = Control.FontFamilyProperty.AddOwner(
|
||||
typeof(MapOverlay), new FrameworkPropertyMetadata((o, e) => ((MapOverlay)o).typeface = null));
|
||||
|
||||
public static readonly DependencyProperty FontStyleProperty = Control.FontStyleProperty.AddOwner(
|
||||
typeof(MapOverlay), new FrameworkPropertyMetadata((o, e) => ((MapOverlay)o).typeface = null));
|
||||
|
||||
public static readonly DependencyProperty FontStretchProperty = Control.FontStretchProperty.AddOwner(
|
||||
typeof(MapOverlay), new FrameworkPropertyMetadata((o, e) => ((MapOverlay)o).typeface = null));
|
||||
|
||||
public static readonly DependencyProperty FontWeightProperty = Control.FontWeightProperty.AddOwner(
|
||||
typeof(MapOverlay), new FrameworkPropertyMetadata((o, e) => ((MapOverlay)o).typeface = null));
|
||||
|
||||
public static readonly DependencyProperty ForegroundProperty = Control.ForegroundProperty.AddOwner(
|
||||
typeof(MapOverlay), new FrameworkPropertyMetadata((o, e) => ((MapOverlay)o).ForegroundChanged((Brush)e.NewValue)));
|
||||
|
||||
public static readonly DependencyProperty StrokeProperty = Shape.StrokeProperty.AddOwner(
|
||||
typeof(MapOverlay), new FrameworkPropertyMetadata((o, e) => ((MapOverlay)o).pen.Brush = (Brush)e.NewValue));
|
||||
|
||||
public static readonly DependencyProperty StrokeThicknessProperty = Shape.StrokeThicknessProperty.AddOwner(
|
||||
typeof(MapOverlay), new FrameworkPropertyMetadata(1d, FrameworkPropertyMetadataOptions.AffectsMeasure, (o, e) => ((MapOverlay)o).pen.Thickness = (double)e.NewValue));
|
||||
|
||||
public static readonly DependencyProperty StrokeDashArrayProperty = Shape.StrokeDashArrayProperty.AddOwner(
|
||||
typeof(MapOverlay), new FrameworkPropertyMetadata((o, e) => ((MapOverlay)o).pen.DashStyle = new DashStyle((DoubleCollection)e.NewValue, ((MapOverlay)o).StrokeDashOffset)));
|
||||
|
||||
public static readonly DependencyProperty StrokeDashOffsetProperty = Shape.StrokeDashOffsetProperty.AddOwner(
|
||||
typeof(MapOverlay), new FrameworkPropertyMetadata((o, e) => ((MapOverlay)o).pen.DashStyle = new DashStyle(((MapOverlay)o).StrokeDashArray, (double)e.NewValue)));
|
||||
|
||||
public static readonly DependencyProperty StrokeDashCapProperty = Shape.StrokeDashCapProperty.AddOwner(
|
||||
typeof(MapOverlay), new FrameworkPropertyMetadata((o, e) => ((MapOverlay)o).pen.DashCap = (PenLineCap)e.NewValue));
|
||||
|
||||
public static readonly DependencyProperty StrokeStartLineCapProperty = Shape.StrokeStartLineCapProperty.AddOwner(
|
||||
typeof(MapOverlay), new FrameworkPropertyMetadata((o, e) => ((MapOverlay)o).pen.StartLineCap = (PenLineCap)e.NewValue));
|
||||
|
||||
public static readonly DependencyProperty StrokeEndLineCapProperty = Shape.StrokeEndLineCapProperty.AddOwner(
|
||||
typeof(MapOverlay), new FrameworkPropertyMetadata((o, e) => ((MapOverlay)o).pen.EndLineCap = (PenLineCap)e.NewValue));
|
||||
|
||||
public static readonly DependencyProperty StrokeLineJoinProperty = Shape.StrokeLineJoinProperty.AddOwner(
|
||||
typeof(MapOverlay), new FrameworkPropertyMetadata((o, e) => ((MapOverlay)o).pen.LineJoin = (PenLineJoin)e.NewValue));
|
||||
|
||||
public static readonly DependencyProperty StrokeMiterLimitProperty = Shape.StrokeMiterLimitProperty.AddOwner(
|
||||
typeof(MapOverlay), new FrameworkPropertyMetadata((o, e) => ((MapOverlay)o).pen.MiterLimit = (double)e.NewValue));
|
||||
|
||||
private Pen pen;
|
||||
private Typeface typeface;
|
||||
|
||||
static MapOverlay()
|
||||
{
|
||||
UIElement.IsHitTestVisibleProperty.OverrideMetadata(
|
||||
typeof(MapOverlay), new FrameworkPropertyMetadata(false));
|
||||
}
|
||||
|
||||
partial void Initialize()
|
||||
{
|
||||
MapPanel.AddParentMapHandlers(this);
|
||||
|
||||
pen = new Pen
|
||||
{
|
||||
Brush = Stroke,
|
||||
Thickness = StrokeThickness,
|
||||
DashStyle = new DashStyle(StrokeDashArray, StrokeDashOffset),
|
||||
DashCap = StrokeDashCap,
|
||||
StartLineCap = StrokeStartLineCap,
|
||||
EndLineCap = StrokeEndLineCap,
|
||||
LineJoin = StrokeLineJoin,
|
||||
MiterLimit = StrokeMiterLimit
|
||||
};
|
||||
}
|
||||
|
||||
public Brush Background
|
||||
{
|
||||
get { return (Brush)GetValue(BackgroundProperty); }
|
||||
set { SetValue(BackgroundProperty, value); }
|
||||
}
|
||||
|
||||
protected Pen Pen
|
||||
{
|
||||
get
|
||||
{
|
||||
if (pen.Brush == null)
|
||||
{
|
||||
pen.Brush = Foreground;
|
||||
}
|
||||
|
||||
return pen;
|
||||
}
|
||||
}
|
||||
|
||||
protected Typeface Typeface
|
||||
{
|
||||
get
|
||||
{
|
||||
if (typeface == null)
|
||||
{
|
||||
typeface = new Typeface(FontFamily, FontStyle, FontWeight, FontStretch);
|
||||
}
|
||||
|
||||
return typeface;
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void OnViewportChanged()
|
||||
{
|
||||
InvalidateVisual();
|
||||
}
|
||||
|
||||
private void OnViewportChanged(object sender, EventArgs e)
|
||||
{
|
||||
OnViewportChanged();
|
||||
}
|
||||
|
||||
void IMapElement.ParentMapChanged(MapBase oldParentMap, MapBase newParentMap)
|
||||
{
|
||||
if (oldParentMap != null)
|
||||
{
|
||||
oldParentMap.ViewportChanged -= OnViewportChanged;
|
||||
}
|
||||
|
||||
if (newParentMap != null)
|
||||
{
|
||||
newParentMap.ViewportChanged += OnViewportChanged;
|
||||
OnViewportChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private void ForegroundChanged(Brush foreground)
|
||||
{
|
||||
if (Stroke == null)
|
||||
{
|
||||
pen.Brush = foreground;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,125 +1,58 @@
|
|||
// WPF MapControl - http://wpfmapcontrol.codeplex.com/
|
||||
// XAML Map Control - http://xamlmapcontrol.codeplex.com/
|
||||
// Copyright © 2012 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
#if WINRT
|
||||
using Windows.UI.Text;
|
||||
using Windows.UI.Xaml.Media;
|
||||
#else
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Shapes;
|
||||
#endif
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class for map overlays with font, background, foreground and stroke properties.
|
||||
/// </summary>
|
||||
public class MapOverlay : MapElement
|
||||
public partial class MapOverlay
|
||||
{
|
||||
public static readonly DependencyProperty FontFamilyProperty = Control.FontFamilyProperty.AddOwner(
|
||||
typeof(MapOverlay), new FrameworkPropertyMetadata((o, e) => ((MapOverlay)o).typeface = null));
|
||||
|
||||
public static readonly DependencyProperty FontStyleProperty = Control.FontStyleProperty.AddOwner(
|
||||
typeof(MapOverlay), new FrameworkPropertyMetadata((o, e) => ((MapOverlay)o).typeface = null));
|
||||
|
||||
public static readonly DependencyProperty FontWeightProperty = Control.FontWeightProperty.AddOwner(
|
||||
typeof(MapOverlay), new FrameworkPropertyMetadata((o, e) => ((MapOverlay)o).typeface = null));
|
||||
|
||||
public static readonly DependencyProperty FontStretchProperty = Control.FontStretchProperty.AddOwner(
|
||||
typeof(MapOverlay), new FrameworkPropertyMetadata((o, e) => ((MapOverlay)o).typeface = null));
|
||||
|
||||
public static readonly DependencyProperty FontSizeProperty = Control.FontSizeProperty.AddOwner(
|
||||
typeof(MapOverlay));
|
||||
|
||||
public static readonly DependencyProperty BackgroundProperty = Control.BackgroundProperty.AddOwner(
|
||||
typeof(MapOverlay));
|
||||
|
||||
public static readonly DependencyProperty ForegroundProperty = Control.ForegroundProperty.AddOwner(
|
||||
typeof(MapOverlay), new FrameworkPropertyMetadata((o, e) => ((MapOverlay)o).ForegroundChanged((Brush)e.NewValue)));
|
||||
|
||||
public static readonly DependencyProperty StrokeProperty = Shape.StrokeProperty.AddOwner(
|
||||
typeof(MapOverlay), new FrameworkPropertyMetadata((o, e) => ((MapOverlay)o).pen.Brush = (Brush)e.NewValue));
|
||||
|
||||
public static readonly DependencyProperty StrokeThicknessProperty = Shape.StrokeThicknessProperty.AddOwner(
|
||||
typeof(MapOverlay), new FrameworkPropertyMetadata(0.5, FrameworkPropertyMetadataOptions.AffectsMeasure, (o, e) => ((MapOverlay)o).pen.Thickness = (double)e.NewValue));
|
||||
|
||||
public static readonly DependencyProperty StrokeDashArrayProperty = Shape.StrokeDashArrayProperty.AddOwner(
|
||||
typeof(MapOverlay), new FrameworkPropertyMetadata((o, e) => ((MapOverlay)o).pen.DashStyle = new DashStyle((DoubleCollection)e.NewValue, ((MapOverlay)o).StrokeDashOffset)));
|
||||
|
||||
public static readonly DependencyProperty StrokeDashOffsetProperty = Shape.StrokeDashOffsetProperty.AddOwner(
|
||||
typeof(MapOverlay), new FrameworkPropertyMetadata((o, e) => ((MapOverlay)o).pen.DashStyle = new DashStyle(((MapOverlay)o).StrokeDashArray, (double)e.NewValue)));
|
||||
|
||||
public static readonly DependencyProperty StrokeDashCapProperty = Shape.StrokeDashCapProperty.AddOwner(
|
||||
typeof(MapOverlay), new FrameworkPropertyMetadata((o, e) => ((MapOverlay)o).pen.DashCap = (PenLineCap)e.NewValue));
|
||||
|
||||
public static readonly DependencyProperty StrokeStartLineCapProperty = Shape.StrokeStartLineCapProperty.AddOwner(
|
||||
typeof(MapOverlay), new FrameworkPropertyMetadata((o, e) => ((MapOverlay)o).pen.StartLineCap = (PenLineCap)e.NewValue));
|
||||
|
||||
public static readonly DependencyProperty StrokeEndLineCapProperty = Shape.StrokeEndLineCapProperty.AddOwner(
|
||||
typeof(MapOverlay), new FrameworkPropertyMetadata((o, e) => ((MapOverlay)o).pen.EndLineCap = (PenLineCap)e.NewValue));
|
||||
|
||||
public static readonly DependencyProperty StrokeLineJoinProperty = Shape.StrokeLineJoinProperty.AddOwner(
|
||||
typeof(MapOverlay), new FrameworkPropertyMetadata((o, e) => ((MapOverlay)o).pen.LineJoin = (PenLineJoin)e.NewValue));
|
||||
|
||||
public static readonly DependencyProperty StrokeMiterLimitProperty = Shape.StrokeMiterLimitProperty.AddOwner(
|
||||
typeof(MapOverlay), new FrameworkPropertyMetadata((o, e) => ((MapOverlay)o).pen.MiterLimit = (double)e.NewValue));
|
||||
|
||||
private readonly Pen pen;
|
||||
private Typeface typeface;
|
||||
|
||||
static MapOverlay()
|
||||
{
|
||||
UIElement.IsHitTestVisibleProperty.OverrideMetadata(
|
||||
typeof(MapOverlay), new FrameworkPropertyMetadata(false));
|
||||
}
|
||||
|
||||
public MapOverlay()
|
||||
{
|
||||
pen = new Pen
|
||||
{
|
||||
Brush = Stroke,
|
||||
Thickness = StrokeThickness,
|
||||
DashStyle = new DashStyle(StrokeDashArray, StrokeDashOffset),
|
||||
DashCap = StrokeDashCap,
|
||||
StartLineCap = StrokeStartLineCap,
|
||||
EndLineCap = StrokeEndLineCap,
|
||||
LineJoin = StrokeLineJoin,
|
||||
MiterLimit = StrokeMiterLimit
|
||||
};
|
||||
IsHitTestVisible = false;
|
||||
Initialize();
|
||||
}
|
||||
|
||||
partial void Initialize();
|
||||
|
||||
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 double FontSize
|
||||
{
|
||||
get { return (double)GetValue(FontSizeProperty); }
|
||||
set { SetValue(FontSizeProperty, value); }
|
||||
}
|
||||
|
||||
public Brush Background
|
||||
public FontStyle FontStyle
|
||||
{
|
||||
get { return (Brush)GetValue(BackgroundProperty); }
|
||||
set { SetValue(BackgroundProperty, value); }
|
||||
get { return (FontStyle)GetValue(FontStyleProperty); }
|
||||
set { SetValue(FontStyleProperty, value); }
|
||||
}
|
||||
|
||||
public FontStretch FontStretch
|
||||
{
|
||||
get { return (FontStretch)GetValue(FontStretchProperty); }
|
||||
set { SetValue(FontStretchProperty, value); }
|
||||
}
|
||||
|
||||
public FontWeight FontWeight
|
||||
{
|
||||
get { return (FontWeight)GetValue(FontWeightProperty); }
|
||||
set { SetValue(FontWeightProperty, value); }
|
||||
}
|
||||
|
||||
public Brush Foreground
|
||||
|
|
@ -181,44 +114,5 @@ namespace MapControl
|
|||
get { return (double)GetValue(StrokeMiterLimitProperty); }
|
||||
set { SetValue(StrokeMiterLimitProperty, value); }
|
||||
}
|
||||
|
||||
protected Pen Pen
|
||||
{
|
||||
get
|
||||
{
|
||||
if (pen.Brush == null)
|
||||
{
|
||||
pen.Brush = Foreground;
|
||||
}
|
||||
|
||||
return pen;
|
||||
}
|
||||
}
|
||||
|
||||
protected Typeface Typeface
|
||||
{
|
||||
get
|
||||
{
|
||||
if (typeface == null)
|
||||
{
|
||||
typeface = new Typeface(FontFamily, FontStyle, FontWeight, FontStretch);
|
||||
}
|
||||
|
||||
return typeface;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnViewportChanged()
|
||||
{
|
||||
InvalidateVisual();
|
||||
}
|
||||
|
||||
private void ForegroundChanged(Brush foreground)
|
||||
{
|
||||
if (Stroke == null)
|
||||
{
|
||||
pen.Brush = foreground;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,39 +1,48 @@
|
|||
// WPF MapControl - http://wpfmapcontrol.codeplex.com/
|
||||
// XAML Map Control - http://xamlmapcontrol.codeplex.com/
|
||||
// Copyright © 2012 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using System;
|
||||
#if WINRT
|
||||
using Windows.Foundation;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Media;
|
||||
#else
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media;
|
||||
#endif
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
/// <summary>
|
||||
/// Positions child elements on a Map. A child element's position is specified by the
|
||||
/// attached property Location, given as geographic location with latitude and longitude.
|
||||
/// The attached property ViewportPosition gets a child element's position in viewport coordinates.
|
||||
/// </summary>
|
||||
public class MapPanel : Panel
|
||||
internal interface IMapElement
|
||||
{
|
||||
internal static readonly DependencyPropertyKey ParentMapPropertyKey = DependencyProperty.RegisterAttachedReadOnly(
|
||||
"ParentMap", typeof(MapBase), typeof(MapPanel),
|
||||
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.Inherits, ParentMapPropertyChanged));
|
||||
void ParentMapChanged(MapBase oldParentMap, MapBase newParentMap);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty ParentMapProperty = ParentMapPropertyKey.DependencyProperty;
|
||||
|
||||
private static readonly DependencyPropertyKey ViewportPositionPropertyKey = DependencyProperty.RegisterAttachedReadOnly(
|
||||
"ViewportPosition", typeof(Point?), typeof(MapPanel),
|
||||
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender, ViewportPositionPropertyChanged));
|
||||
|
||||
public static readonly DependencyProperty ViewportPositionProperty = ViewportPositionPropertyKey.DependencyProperty;
|
||||
/// <summary>
|
||||
/// Positions child elements on a Map, at a position specified by the attached property Location.
|
||||
/// The Location is transformed into a viewport position by the MapBase.LocationToViewportPoint
|
||||
/// method and then applied to the RenderTransform as an appropriate TranslateTransform.
|
||||
/// </summary>
|
||||
public class MapPanel : Panel, IMapElement
|
||||
{
|
||||
public static readonly DependencyProperty ParentMapProperty = DependencyProperty.RegisterAttached(
|
||||
"ParentMap", typeof(MapBase), typeof(MapPanel), new PropertyMetadata(null, ParentMapPropertyChanged));
|
||||
|
||||
public static readonly DependencyProperty LocationProperty = DependencyProperty.RegisterAttached(
|
||||
"Location", typeof(Location), typeof(MapPanel),
|
||||
new FrameworkPropertyMetadata(LocationPropertyChanged));
|
||||
"Location", typeof(Location), typeof(MapPanel), new PropertyMetadata(null, LocationPropertyChanged));
|
||||
|
||||
public MapBase ParentMap
|
||||
public MapPanel()
|
||||
{
|
||||
get { return (MapBase)GetValue(ParentMapProperty); }
|
||||
AddParentMapHandlers(this);
|
||||
}
|
||||
|
||||
public static void AddParentMapHandlers(FrameworkElement element)
|
||||
{
|
||||
element.Loaded += (o, e) => element.SetValue(ParentMapProperty, FindParentMap(element));
|
||||
element.Unloaded += (o, e) => element.ClearValue(ParentMapProperty);
|
||||
}
|
||||
|
||||
public static MapBase GetParentMap(UIElement element)
|
||||
|
|
@ -41,11 +50,6 @@ namespace MapControl
|
|||
return (MapBase)element.GetValue(ParentMapProperty);
|
||||
}
|
||||
|
||||
public static Point? GetViewportPosition(UIElement element)
|
||||
{
|
||||
return (Point?)element.GetValue(ViewportPositionProperty);
|
||||
}
|
||||
|
||||
public static Location GetLocation(UIElement element)
|
||||
{
|
||||
return (Location)element.GetValue(LocationProperty);
|
||||
|
|
@ -58,11 +62,9 @@ namespace MapControl
|
|||
|
||||
protected override Size MeasureOverride(Size availableSize)
|
||||
{
|
||||
Size infiniteSize = new Size(double.PositiveInfinity, double.PositiveInfinity);
|
||||
|
||||
foreach (UIElement element in InternalChildren)
|
||||
foreach (UIElement element in Children)
|
||||
{
|
||||
element.Measure(infiniteSize);
|
||||
element.Measure(availableSize);
|
||||
}
|
||||
|
||||
return new Size();
|
||||
|
|
@ -70,18 +72,66 @@ namespace MapControl
|
|||
|
||||
protected override Size ArrangeOverride(Size finalSize)
|
||||
{
|
||||
foreach (UIElement element in InternalChildren)
|
||||
{
|
||||
Point? viewportPosition = GetViewportPosition(element);
|
||||
var parentMap = GetParentMap(this);
|
||||
|
||||
if (viewportPosition.HasValue)
|
||||
foreach (UIElement element in Children)
|
||||
{
|
||||
var location = GetLocation(element);
|
||||
|
||||
if (location != null)
|
||||
{
|
||||
ArrangeElement(element, viewportPosition.Value);
|
||||
SetViewportPosition(element, parentMap, location);
|
||||
}
|
||||
else
|
||||
|
||||
var frameworkElement = element as FrameworkElement;
|
||||
var rect = new Rect(0d, 0d, element.DesiredSize.Width, element.DesiredSize.Height);
|
||||
|
||||
if (frameworkElement != null)
|
||||
{
|
||||
element.Arrange(new Rect(finalSize));
|
||||
switch (frameworkElement.HorizontalAlignment)
|
||||
{
|
||||
case HorizontalAlignment.Center:
|
||||
rect.X = ((location == null ? finalSize.Width : 0) - rect.Width) / 2d;
|
||||
break;
|
||||
|
||||
case HorizontalAlignment.Right:
|
||||
rect.X = (location == null ? finalSize.Width : 0) - rect.Width;
|
||||
break;
|
||||
|
||||
case HorizontalAlignment.Stretch:
|
||||
if (location == null)
|
||||
{
|
||||
rect.Width = finalSize.Width;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
switch (frameworkElement.VerticalAlignment)
|
||||
{
|
||||
case VerticalAlignment.Center:
|
||||
rect.Y = ((location == null ? finalSize.Height : 0) - rect.Height) / 2d;
|
||||
break;
|
||||
|
||||
case VerticalAlignment.Bottom:
|
||||
rect.Y = (location == null ? finalSize.Height : 0) - rect.Height;
|
||||
break;
|
||||
|
||||
case VerticalAlignment.Stretch:
|
||||
if (location == null)
|
||||
{
|
||||
rect.Height = finalSize.Height;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
element.Arrange(rect);
|
||||
}
|
||||
|
||||
return finalSize;
|
||||
|
|
@ -89,13 +139,15 @@ namespace MapControl
|
|||
|
||||
protected virtual void OnViewportChanged()
|
||||
{
|
||||
foreach (UIElement element in InternalChildren)
|
||||
var parentMap = GetParentMap(this);
|
||||
|
||||
foreach (UIElement element in Children)
|
||||
{
|
||||
Location location = GetLocation(element);
|
||||
var location = GetLocation(element);
|
||||
|
||||
if (location != null)
|
||||
{
|
||||
SetViewportPosition(element, ParentMap, location);
|
||||
SetViewportPosition(element, parentMap, location);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -105,49 +157,38 @@ namespace MapControl
|
|||
OnViewportChanged();
|
||||
}
|
||||
|
||||
private static void ParentMapPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
|
||||
void IMapElement.ParentMapChanged(MapBase oldParentMap, MapBase newParentMap)
|
||||
{
|
||||
MapPanel mapPanel = obj as MapPanel;
|
||||
|
||||
if (mapPanel != null)
|
||||
if (oldParentMap != null && oldParentMap != this)
|
||||
{
|
||||
MapBase oldParentMap = e.OldValue as MapBase;
|
||||
MapBase newParentMap = e.NewValue as MapBase;
|
||||
oldParentMap.ViewportChanged -= OnViewportChanged;
|
||||
}
|
||||
|
||||
if (oldParentMap != null && oldParentMap != mapPanel)
|
||||
{
|
||||
oldParentMap.ViewportChanged -= mapPanel.OnViewportChanged;
|
||||
}
|
||||
|
||||
if (newParentMap != null && newParentMap != mapPanel)
|
||||
{
|
||||
newParentMap.ViewportChanged += mapPanel.OnViewportChanged;
|
||||
}
|
||||
if (newParentMap != null && newParentMap != this)
|
||||
{
|
||||
newParentMap.ViewportChanged += OnViewportChanged;
|
||||
OnViewportChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private static void ViewportPositionPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
|
||||
private static MapBase FindParentMap(DependencyObject obj)
|
||||
{
|
||||
UIElement element = obj as UIElement;
|
||||
return (obj == null || obj is MapBase) ? (MapBase)obj : FindParentMap(VisualTreeHelper.GetParent(obj));
|
||||
}
|
||||
|
||||
private static void ParentMapPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
var element = obj as IMapElement;
|
||||
|
||||
if (element != null)
|
||||
{
|
||||
Point? viewportPosition = (Point?)e.NewValue;
|
||||
|
||||
if (viewportPosition.HasValue)
|
||||
{
|
||||
ArrangeElement(element, viewportPosition.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
element.Arrange(new Rect());
|
||||
}
|
||||
element.ParentMapChanged(e.OldValue as MapBase, e.NewValue as MapBase);
|
||||
}
|
||||
}
|
||||
|
||||
private static void LocationPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
UIElement element = obj as UIElement;
|
||||
var element = obj as UIElement;
|
||||
|
||||
if (element != null)
|
||||
{
|
||||
|
|
@ -157,54 +198,43 @@ namespace MapControl
|
|||
|
||||
private static void SetViewportPosition(UIElement element, MapBase parentMap, Location location)
|
||||
{
|
||||
Point? viewportPosition = null;
|
||||
Transform transform = null;
|
||||
|
||||
if (parentMap != null && location != null)
|
||||
{
|
||||
viewportPosition = parentMap.LocationToViewportPoint(location);
|
||||
Point position = parentMap.LocationToViewportPoint(location);
|
||||
transform = new TranslateTransform { X = position.X, Y = position.Y };
|
||||
}
|
||||
|
||||
element.SetValue(ViewportPositionPropertyKey, viewportPosition);
|
||||
}
|
||||
var transformGroup = element.RenderTransform as TransformGroup;
|
||||
|
||||
private static void ArrangeElement(UIElement element, Point position)
|
||||
{
|
||||
Rect rect = new Rect(position, element.DesiredSize);
|
||||
|
||||
if (element is FrameworkElement)
|
||||
if (transformGroup != null)
|
||||
{
|
||||
switch (((FrameworkElement)element).HorizontalAlignment)
|
||||
if (transform == null)
|
||||
{
|
||||
case HorizontalAlignment.Center:
|
||||
rect.X -= rect.Width / 2d;
|
||||
break;
|
||||
case HorizontalAlignment.Right:
|
||||
rect.X -= rect.Width;
|
||||
break;
|
||||
case HorizontalAlignment.Stretch:
|
||||
rect.X = 0d;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
transform = new TranslateTransform();
|
||||
}
|
||||
|
||||
switch (((FrameworkElement)element).VerticalAlignment)
|
||||
var transformIndex = transformGroup.Children.Count - 1;
|
||||
|
||||
if (transformIndex >= 0 &&
|
||||
transformGroup.Children[transformIndex] is TranslateTransform)
|
||||
{
|
||||
case VerticalAlignment.Center:
|
||||
rect.Y -= rect.Height / 2d;
|
||||
break;
|
||||
case VerticalAlignment.Bottom:
|
||||
rect.Y -= rect.Height;
|
||||
break;
|
||||
case VerticalAlignment.Stretch:
|
||||
rect.Y = 0d;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
transformGroup.Children[transformIndex] = transform;
|
||||
}
|
||||
else
|
||||
{
|
||||
transformGroup.Children.Add(transform);
|
||||
}
|
||||
}
|
||||
|
||||
element.Arrange(rect);
|
||||
else if (transform != null)
|
||||
{
|
||||
element.RenderTransform = transform;
|
||||
}
|
||||
else
|
||||
{
|
||||
element.ClearValue(UIElement.RenderTransformProperty);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,35 +1,14 @@
|
|||
// WPF MapControl - http://wpfmapcontrol.codeplex.com/
|
||||
// XAML Map Control - http://xamlmapcontrol.codeplex.com/
|
||||
// Copyright © 2012 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Shapes;
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
/// <summary>
|
||||
/// A closed map polygon, defined by a collection of geographic locations in the Locations property.
|
||||
/// </summary>
|
||||
public class MapPolygon : MapPolyline
|
||||
{
|
||||
public static readonly DependencyProperty FillProperty = Shape.FillProperty.AddOwner(
|
||||
typeof(MapPolygon), new FrameworkPropertyMetadata((o, e) => ((MapPolygon)o).Drawing.Brush = (Brush)e.NewValue));
|
||||
|
||||
public MapPolygon()
|
||||
protected override bool IsClosed
|
||||
{
|
||||
Drawing.Brush = Fill;
|
||||
}
|
||||
|
||||
public Brush Fill
|
||||
{
|
||||
get { return (Brush)GetValue(FillProperty); }
|
||||
set { SetValue(FillProperty, value); }
|
||||
}
|
||||
|
||||
protected override void UpdateGeometry()
|
||||
{
|
||||
UpdateGeometry(true);
|
||||
get { return true; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
25
MapControl/MapPolyline.Silverlight.cs
Normal file
25
MapControl/MapPolyline.Silverlight.cs
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
// XAML Map Control - http://xamlmapcontrol.codeplex.com/
|
||||
// Copyright © 2012 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using System;
|
||||
using System.Windows;
|
||||
using System.Windows.Shapes;
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
public partial class MapPolyline : Path
|
||||
{
|
||||
partial void Initialize()
|
||||
{
|
||||
Data = Geometry;
|
||||
}
|
||||
|
||||
protected override Size MeasureOverride(Size constraint)
|
||||
{
|
||||
return new Size(
|
||||
Math.Max(Geometry.Bounds.Width, Geometry.Bounds.Right),
|
||||
Math.Max(Geometry.Bounds.Height, Geometry.Bounds.Bottom));
|
||||
}
|
||||
}
|
||||
}
|
||||
18
MapControl/MapPolyline.WPF.cs
Normal file
18
MapControl/MapPolyline.WPF.cs
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
// XAML Map Control - http://xamlmapcontrol.codeplex.com/
|
||||
// Copyright © 2012 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Shapes;
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
public partial class MapPolyline : Shape
|
||||
{
|
||||
protected override Geometry DefiningGeometry
|
||||
{
|
||||
get { return Geometry; }
|
||||
}
|
||||
}
|
||||
}
|
||||
16
MapControl/MapPolyline.WinRT.cs
Normal file
16
MapControl/MapPolyline.WinRT.cs
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
// XAML Map Control - http://xamlmapcontrol.codeplex.com/
|
||||
// Copyright © 2012 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using Windows.UI.Xaml.Shapes;
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
public partial class MapPolyline : Path
|
||||
{
|
||||
partial void Initialize()
|
||||
{
|
||||
Data = Geometry;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,143 +1,33 @@
|
|||
// WPF MapControl - http://wpfmapcontrol.codeplex.com/
|
||||
// XAML Map Control - http://xamlmapcontrol.codeplex.com/
|
||||
// Copyright © 2012 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
#if WINRT
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Media;
|
||||
#else
|
||||
using System.Windows;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Shapes;
|
||||
#endif
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
/// <summary>
|
||||
/// An open map polygon, defined by a collection of geographic locations in the Locations property.
|
||||
/// </summary>
|
||||
public class MapPolyline : FrameworkElement
|
||||
public partial class MapPolyline : IMapElement
|
||||
{
|
||||
public static readonly DependencyProperty StrokeProperty = Shape.StrokeProperty.AddOwner(
|
||||
typeof(MapPolyline), new FrameworkPropertyMetadata((o, e) => ((MapPolyline)o).Drawing.Pen.Brush = (Brush)e.NewValue));
|
||||
|
||||
public static readonly DependencyProperty StrokeThicknessProperty = Shape.StrokeThicknessProperty.AddOwner(
|
||||
typeof(MapPolyline));
|
||||
|
||||
public static readonly DependencyProperty StrokeDashArrayProperty = Shape.StrokeDashArrayProperty.AddOwner(
|
||||
typeof(MapPolyline), new FrameworkPropertyMetadata((o, e) => ((MapPolyline)o).Drawing.Pen.DashStyle = new DashStyle((DoubleCollection)e.NewValue, ((MapPolyline)o).StrokeDashOffset)));
|
||||
|
||||
public static readonly DependencyProperty StrokeDashOffsetProperty = Shape.StrokeDashOffsetProperty.AddOwner(
|
||||
typeof(MapPolyline), new FrameworkPropertyMetadata((o, e) => ((MapPolyline)o).Drawing.Pen.DashStyle = new DashStyle(((MapPolyline)o).StrokeDashArray, (double)e.NewValue)));
|
||||
|
||||
public static readonly DependencyProperty StrokeDashCapProperty = Shape.StrokeDashCapProperty.AddOwner(
|
||||
typeof(MapPolyline), new FrameworkPropertyMetadata((o, e) => ((MapPolyline)o).Drawing.Pen.DashCap = (PenLineCap)e.NewValue));
|
||||
|
||||
public static readonly DependencyProperty StrokeStartLineCapProperty = Shape.StrokeStartLineCapProperty.AddOwner(
|
||||
typeof(MapPolyline), new FrameworkPropertyMetadata((o, e) => ((MapPolyline)o).Drawing.Pen.StartLineCap = (PenLineCap)e.NewValue));
|
||||
|
||||
public static readonly DependencyProperty StrokeEndLineCapProperty = Shape.StrokeEndLineCapProperty.AddOwner(
|
||||
typeof(MapPolyline), new FrameworkPropertyMetadata((o, e) => ((MapPolyline)o).Drawing.Pen.EndLineCap = (PenLineCap)e.NewValue));
|
||||
|
||||
public static readonly DependencyProperty StrokeLineJoinProperty = Shape.StrokeLineJoinProperty.AddOwner(
|
||||
typeof(MapPolyline), new FrameworkPropertyMetadata((o, e) => ((MapPolyline)o).Drawing.Pen.LineJoin = (PenLineJoin)e.NewValue));
|
||||
|
||||
public static readonly DependencyProperty StrokeMiterLimitProperty = Shape.StrokeMiterLimitProperty.AddOwner(
|
||||
typeof(MapPolyline), new FrameworkPropertyMetadata((o, e) => ((MapPolyline)o).Drawing.Pen.MiterLimit = (double)e.NewValue));
|
||||
|
||||
public static readonly DependencyProperty TransformStrokeProperty = DependencyProperty.Register(
|
||||
"TransformStroke", typeof(bool), typeof(MapPolyline), new FrameworkPropertyMetadata((o, e) => ((MapPolyline)o).SetPenThicknessBinding()));
|
||||
|
||||
public static readonly DependencyProperty LocationsProperty = DependencyProperty.Register(
|
||||
"Locations", typeof(LocationCollection), typeof(MapPolyline), new FrameworkPropertyMetadata((o, e) => ((MapPolyline)o).UpdateGeometry()));
|
||||
"Locations", typeof(LocationCollection), typeof(MapPolyline),
|
||||
new PropertyMetadata(null, LocationsPropertyChanged));
|
||||
|
||||
protected readonly GeometryDrawing Drawing = new GeometryDrawing();
|
||||
|
||||
static MapPolyline()
|
||||
{
|
||||
MapPanel.ParentMapPropertyKey.OverrideMetadata(
|
||||
typeof(MapPolyline),
|
||||
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.Inherits, ParentMapPropertyChanged));
|
||||
}
|
||||
protected PathGeometry Geometry = new PathGeometry();
|
||||
|
||||
public MapPolyline()
|
||||
{
|
||||
Drawing.Pen = new Pen
|
||||
{
|
||||
Brush = Stroke,
|
||||
Thickness = StrokeThickness,
|
||||
DashStyle = new DashStyle(StrokeDashArray, StrokeDashOffset),
|
||||
DashCap = StrokeDashCap,
|
||||
StartLineCap = StrokeStartLineCap,
|
||||
EndLineCap = StrokeEndLineCap,
|
||||
LineJoin = StrokeLineJoin,
|
||||
MiterLimit = StrokeMiterLimit
|
||||
};
|
||||
MapPanel.AddParentMapHandlers(this);
|
||||
Initialize();
|
||||
}
|
||||
|
||||
public MapBase ParentMap
|
||||
{
|
||||
get { return MapPanel.GetParentMap(this); }
|
||||
}
|
||||
|
||||
public Brush Stroke
|
||||
{
|
||||
get { return (Brush)GetValue(StrokeProperty); }
|
||||
set { SetValue(StrokeProperty, value); }
|
||||
}
|
||||
|
||||
public double StrokeThickness
|
||||
{
|
||||
get { return (double)GetValue(StrokeThicknessProperty); }
|
||||
set { SetValue(StrokeThicknessProperty, value); }
|
||||
}
|
||||
|
||||
public DoubleCollection StrokeDashArray
|
||||
{
|
||||
get { return (DoubleCollection)GetValue(StrokeDashArrayProperty); }
|
||||
set { SetValue(StrokeDashArrayProperty, value); }
|
||||
}
|
||||
|
||||
public double StrokeDashOffset
|
||||
{
|
||||
get { return (double)GetValue(StrokeDashOffsetProperty); }
|
||||
set { SetValue(StrokeDashOffsetProperty, value); }
|
||||
}
|
||||
|
||||
public PenLineCap StrokeDashCap
|
||||
{
|
||||
get { return (PenLineCap)GetValue(StrokeDashCapProperty); }
|
||||
set { SetValue(StrokeDashCapProperty, value); }
|
||||
}
|
||||
|
||||
public PenLineCap StrokeStartLineCap
|
||||
{
|
||||
get { return (PenLineCap)GetValue(StrokeStartLineCapProperty); }
|
||||
set { SetValue(StrokeStartLineCapProperty, value); }
|
||||
}
|
||||
|
||||
public PenLineCap StrokeEndLineCap
|
||||
{
|
||||
get { return (PenLineCap)GetValue(StrokeEndLineCapProperty); }
|
||||
set { SetValue(StrokeEndLineCapProperty, value); }
|
||||
}
|
||||
|
||||
public PenLineJoin StrokeLineJoin
|
||||
{
|
||||
get { return (PenLineJoin)GetValue(StrokeLineJoinProperty); }
|
||||
set { SetValue(StrokeLineJoinProperty, value); }
|
||||
}
|
||||
|
||||
public double StrokeMiterLimit
|
||||
{
|
||||
get { return (double)GetValue(StrokeMiterLimitProperty); }
|
||||
set { SetValue(StrokeMiterLimitProperty, value); }
|
||||
}
|
||||
|
||||
public bool TransformStroke
|
||||
{
|
||||
get { return (bool)GetValue(TransformStrokeProperty); }
|
||||
set { SetValue(TransformStrokeProperty, value); }
|
||||
}
|
||||
partial void Initialize();
|
||||
|
||||
public LocationCollection Locations
|
||||
{
|
||||
|
|
@ -145,95 +35,48 @@ namespace MapControl
|
|||
set { SetValue(LocationsProperty, value); }
|
||||
}
|
||||
|
||||
public PathGeometry TransformedGeometry
|
||||
protected virtual bool IsClosed
|
||||
{
|
||||
get { return Drawing.Geometry as PathGeometry; }
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
public double TransformedStrokeThickness
|
||||
protected virtual void UpdateGeometry(MapBase parentMap, LocationCollection locations)
|
||||
{
|
||||
get { return Drawing.Pen.Thickness; }
|
||||
}
|
||||
|
||||
protected override void OnRender(DrawingContext drawingContext)
|
||||
{
|
||||
drawingContext.DrawDrawing(Drawing);
|
||||
}
|
||||
|
||||
protected virtual void UpdateGeometry()
|
||||
{
|
||||
UpdateGeometry(false);
|
||||
}
|
||||
|
||||
protected void UpdateGeometry(bool closed)
|
||||
{
|
||||
if (ParentMap != null && Locations != null && Locations.Count > 0)
|
||||
if (parentMap != null && locations != null && locations.Count > 0)
|
||||
{
|
||||
Drawing.Geometry = CreateGeometry(Locations, closed);
|
||||
}
|
||||
else
|
||||
{
|
||||
Drawing.Geometry = null;
|
||||
}
|
||||
}
|
||||
|
||||
private Geometry CreateGeometry(LocationCollection locations, bool closed)
|
||||
{
|
||||
StreamGeometry geometry = new StreamGeometry
|
||||
{
|
||||
Transform = ParentMap.ViewportTransform
|
||||
};
|
||||
|
||||
using (StreamGeometryContext sgc = geometry.Open())
|
||||
{
|
||||
sgc.BeginFigure(ParentMap.MapTransform.Transform(locations.First()), closed, closed);
|
||||
|
||||
if (Locations.Count > 1)
|
||||
var figure = new PathFigure
|
||||
{
|
||||
sgc.PolyLineTo(ParentMap.MapTransform.Transform(locations.Skip(1)), true, true);
|
||||
StartPoint = parentMap.MapTransform.Transform(locations[0]),
|
||||
IsClosed = IsClosed,
|
||||
IsFilled = IsClosed
|
||||
};
|
||||
|
||||
if (locations.Count > 1)
|
||||
{
|
||||
var segment = new PolyLineSegment();
|
||||
|
||||
foreach (Location location in locations.Skip(1))
|
||||
{
|
||||
segment.Points.Add(parentMap.MapTransform.Transform(location));
|
||||
}
|
||||
|
||||
figure.Segments.Add(segment);
|
||||
}
|
||||
}
|
||||
|
||||
return geometry;
|
||||
}
|
||||
|
||||
private void SetPenThicknessBinding()
|
||||
{
|
||||
BindingBase binding = new Binding { Source = this, Path = new PropertyPath(MapPolyline.StrokeThicknessProperty)};
|
||||
|
||||
if (TransformStroke && ParentMap != null)
|
||||
{
|
||||
MultiBinding multiBinding = new MultiBinding { Converter = new PenThicknessConverter() };
|
||||
multiBinding.Bindings.Add(binding);
|
||||
multiBinding.Bindings.Add(new Binding { Source = ParentMap, Path = new PropertyPath(MapBase.CenterScaleProperty)});
|
||||
binding = multiBinding;
|
||||
}
|
||||
|
||||
BindingOperations.SetBinding(Drawing.Pen, Pen.ThicknessProperty, binding);
|
||||
}
|
||||
|
||||
private static void ParentMapPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
MapPolyline polyline = obj as MapPolyline;
|
||||
|
||||
if (polyline != null)
|
||||
{
|
||||
polyline.UpdateGeometry();
|
||||
polyline.SetPenThicknessBinding();
|
||||
Geometry.Figures.Add(figure);
|
||||
Geometry.Transform = parentMap.ViewportTransform;
|
||||
}
|
||||
}
|
||||
|
||||
private class PenThicknessConverter : IMultiValueConverter
|
||||
void IMapElement.ParentMapChanged(MapBase oldParentMap, MapBase newParentMap)
|
||||
{
|
||||
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
return (double)values[0] * (double)values[1] * MapBase.MeterPerDegree;
|
||||
}
|
||||
UpdateGeometry(newParentMap, Locations);
|
||||
}
|
||||
|
||||
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
private static void LocationsPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
var polyline = (MapPolyline)obj;
|
||||
polyline.UpdateGeometry(MapPanel.GetParentMap(polyline), (LocationCollection)e.NewValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// WPF MapControl - http://wpfmapcontrol.codeplex.com/
|
||||
// XAML Map Control - http://xamlmapcontrol.codeplex.com/
|
||||
// Copyright © 2012 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
|
|
@ -46,42 +46,56 @@ namespace MapControl
|
|||
|
||||
protected override Size MeasureOverride(Size availableSize)
|
||||
{
|
||||
double scale = ParentMap.CenterScale; // px/m
|
||||
length = MinWidth / scale;
|
||||
double magnitude = Math.Pow(10d, Math.Floor(Math.Log10(length)));
|
||||
var parentMap = MapPanel.GetParentMap(this);
|
||||
|
||||
if (length / magnitude < 2d)
|
||||
if (parentMap != null && parentMap.CenterScale > 0d)
|
||||
{
|
||||
length = 2d * magnitude;
|
||||
}
|
||||
else if (length / magnitude < 5d)
|
||||
{
|
||||
length = 5d * magnitude;
|
||||
length = MinWidth / parentMap.CenterScale;
|
||||
var magnitude = Math.Pow(10d, Math.Floor(Math.Log10(length)));
|
||||
|
||||
if (length / magnitude < 2d)
|
||||
{
|
||||
length = 2d * magnitude;
|
||||
}
|
||||
else if (length / magnitude < 5d)
|
||||
{
|
||||
length = 5d * magnitude;
|
||||
}
|
||||
else
|
||||
{
|
||||
length = 10d * magnitude;
|
||||
}
|
||||
|
||||
size.Width = length * parentMap.CenterScale + StrokeThickness + Padding.Left + Padding.Right;
|
||||
size.Height = FontSize + 2d * StrokeThickness + Padding.Top + Padding.Bottom;
|
||||
}
|
||||
else
|
||||
{
|
||||
length = 10d * magnitude;
|
||||
size.Width = size.Height = 0d;
|
||||
}
|
||||
|
||||
size.Width = length * scale + StrokeThickness + Padding.Left + Padding.Right;
|
||||
size.Height = FontSize + 2d * StrokeThickness + Padding.Top + Padding.Bottom;
|
||||
return size;
|
||||
}
|
||||
|
||||
protected override void OnRender(DrawingContext drawingContext)
|
||||
{
|
||||
double x1 = Padding.Left + StrokeThickness / 2d;
|
||||
double x2 = size.Width - Padding.Right - StrokeThickness / 2d;
|
||||
double y1 = size.Height / 2d;
|
||||
double y2 = size.Height - Padding.Bottom - StrokeThickness / 2d;
|
||||
string text = length >= 1000d ? string.Format("{0:0} km", length / 1000d) : string.Format("{0:0} m", length);
|
||||
var parentMap = MapPanel.GetParentMap(this);
|
||||
|
||||
drawingContext.DrawRectangle(Background ?? ParentMap.Background, null, new Rect(size));
|
||||
drawingContext.DrawLine(Pen, new Point(x1, y1), new Point(x1, y2));
|
||||
drawingContext.DrawLine(Pen, new Point(x2, y1), new Point(x2, y2));
|
||||
drawingContext.DrawLine(Pen, new Point(x1, y2), new Point(x2, y2));
|
||||
drawingContext.DrawGlyphRun(Foreground,
|
||||
GlyphRunText.Create(text, Typeface, FontSize, new Vector(size.Width / 2d, y1 - StrokeThickness - 1d)));
|
||||
if (parentMap != null)
|
||||
{
|
||||
var x1 = Padding.Left + StrokeThickness / 2d;
|
||||
var x2 = size.Width - Padding.Right - StrokeThickness / 2d;
|
||||
var y1 = size.Height / 2d;
|
||||
var y2 = size.Height - Padding.Bottom - StrokeThickness / 2d;
|
||||
var text = length >= 1000d ? string.Format("{0:0} km", length / 1000d) : string.Format("{0:0} m", length);
|
||||
|
||||
drawingContext.DrawRectangle(Background ?? parentMap.Background, null, new Rect(size));
|
||||
drawingContext.DrawLine(Pen, new Point(x1, y1), new Point(x1, y2));
|
||||
drawingContext.DrawLine(Pen, new Point(x2, y1), new Point(x2, y2));
|
||||
drawingContext.DrawLine(Pen, new Point(x1, y2), new Point(x2, y2));
|
||||
drawingContext.DrawGlyphRun(Foreground,
|
||||
GlyphRunText.Create(text, Typeface, FontSize, new Vector(size.Width / 2d, y1 - StrokeThickness - 1d)));
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnViewportChanged()
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
// WPF MapControl - http://wpfmapcontrol.codeplex.com/
|
||||
// XAML Map Control - http://xamlmapcontrol.codeplex.com/
|
||||
// Copyright © 2012 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
#if WINRT
|
||||
using Windows.Foundation;
|
||||
#else
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
#endif
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
|
|
@ -35,22 +36,6 @@ namespace MapControl
|
|||
/// <summary>
|
||||
/// Transforms a cartesian coordinate point to a geographic location.
|
||||
/// </summary>
|
||||
public abstract Location TransformBack(Point point);
|
||||
|
||||
/// <summary>
|
||||
/// Transforms a collection of geographic locations to a collection of cartesian coordinate points.
|
||||
/// </summary>
|
||||
public PointCollection Transform(IEnumerable<Location> locations)
|
||||
{
|
||||
return new PointCollection(locations.Select(l => Transform(l)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Transforms a collection of cartesian coordinate points to a collections of geographic location.
|
||||
/// </summary>
|
||||
public LocationCollection TransformBack(IEnumerable<Point> points)
|
||||
{
|
||||
return new LocationCollection(points.Select(p => TransformBack(p)));
|
||||
}
|
||||
public abstract Location Transform(Point point);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
67
MapControl/MatrixEx.cs
Normal file
67
MapControl/MatrixEx.cs
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
// XAML Map Control - http://xamlmapcontrol.codeplex.com/
|
||||
// Copyright © 2012 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using System;
|
||||
#if WINRT
|
||||
using Windows.UI.Xaml.Media;
|
||||
#else
|
||||
using System.Windows.Media;
|
||||
#endif
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
public static class MatrixEx
|
||||
{
|
||||
public static Matrix Translate(this Matrix matrix, double offsetX, double offsetY)
|
||||
{
|
||||
matrix.OffsetX += offsetX;
|
||||
matrix.OffsetY += offsetY;
|
||||
return matrix;
|
||||
}
|
||||
|
||||
public static Matrix Scale(this Matrix matrix, double scaleX, double scaleY)
|
||||
{
|
||||
return Multiply(matrix, new Matrix(scaleX, 0d, 0d, scaleY, 0d, 0d));
|
||||
}
|
||||
|
||||
public static Matrix Rotate(this Matrix matrix, double angle)
|
||||
{
|
||||
angle = (angle % 360d) / 180d * Math.PI;
|
||||
var cos = Math.Cos(angle);
|
||||
var sin = Math.Sin(angle);
|
||||
return Multiply(matrix, new Matrix(cos, sin, -sin, cos, 0d, 0d));
|
||||
}
|
||||
|
||||
public static Matrix RotateAt(this Matrix matrix, double angle, double centerX, double centerY)
|
||||
{
|
||||
angle = (angle % 360d) / 180d * Math.PI;
|
||||
var cos = Math.Cos(angle);
|
||||
var sin = Math.Sin(angle);
|
||||
var offsetX = centerX * (1d - cos) + centerY * sin;
|
||||
var offsetY = centerY * (1d - cos) - centerX * sin;
|
||||
return Multiply(matrix, new Matrix(cos, sin, -sin, cos, offsetX, offsetY));
|
||||
}
|
||||
|
||||
public static Matrix Invert(this Matrix matrix)
|
||||
{
|
||||
var determinant = matrix.M11 * matrix.M22 - matrix.M12 * matrix.M21;
|
||||
return new Matrix(
|
||||
matrix.M22 / determinant, -matrix.M12 / determinant,
|
||||
-matrix.M21 / determinant, matrix.M11 / determinant,
|
||||
(matrix.M21 * matrix.OffsetY - matrix.M22 * matrix.OffsetX) / determinant,
|
||||
(matrix.M12 * matrix.OffsetX - matrix.M11 * matrix.OffsetY) / determinant);
|
||||
}
|
||||
|
||||
public static Matrix Multiply(this Matrix matrix1, Matrix matrix2)
|
||||
{
|
||||
return new Matrix(
|
||||
matrix1.M11 * matrix2.M11 + matrix1.M12 * matrix2.M21,
|
||||
matrix1.M11 * matrix2.M12 + matrix1.M12 * matrix2.M22,
|
||||
matrix1.M21 * matrix2.M11 + matrix1.M22 * matrix2.M21,
|
||||
matrix1.M21 * matrix2.M12 + matrix1.M22 * matrix2.M22,
|
||||
(matrix2.M11 * matrix1.OffsetX + matrix2.M21 * matrix1.OffsetY) + matrix2.OffsetX,
|
||||
(matrix2.M12 * matrix1.OffsetX + matrix2.M22 * matrix1.OffsetY) + matrix2.OffsetY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,9 +1,13 @@
|
|||
// WPF MapControl - http://wpfmapcontrol.codeplex.com/
|
||||
// XAML Map Control - http://xamlmapcontrol.codeplex.com/
|
||||
// Copyright © 2012 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using System;
|
||||
#if WINRT
|
||||
using Windows.Foundation;
|
||||
#else
|
||||
using System.Windows;
|
||||
#endif
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
|
|
@ -35,28 +39,31 @@ namespace MapControl
|
|||
|
||||
public override Point Transform(Location location)
|
||||
{
|
||||
Point result = new Point(location.Longitude, 0d);
|
||||
|
||||
if (location.Latitude <= -90d)
|
||||
if (double.IsNaN(location.Y))
|
||||
{
|
||||
result.Y = double.NegativeInfinity;
|
||||
}
|
||||
else if (location.Latitude >= 90d)
|
||||
{
|
||||
result.Y = double.PositiveInfinity;
|
||||
}
|
||||
else
|
||||
{
|
||||
double lat = location.Latitude * Math.PI / 180d;
|
||||
result.Y = (Math.Log(Math.Tan(lat) + 1d / Math.Cos(lat))) / Math.PI * 180d;
|
||||
if (location.Latitude <= -90d)
|
||||
{
|
||||
location.Y = double.NegativeInfinity;
|
||||
}
|
||||
else if (location.Latitude >= 90d)
|
||||
{
|
||||
location.Y = double.PositiveInfinity;
|
||||
}
|
||||
else
|
||||
{
|
||||
var lat = location.Latitude * Math.PI / 180d;
|
||||
location.Y = (Math.Log(Math.Tan(lat) + 1d / Math.Cos(lat))) / Math.PI * 180d;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
return new Point(location.Longitude, location.Y);
|
||||
}
|
||||
|
||||
public override Location TransformBack(Point point)
|
||||
public override Location Transform(Point point)
|
||||
{
|
||||
return new Location(Math.Atan(Math.Sinh(point.Y * Math.PI / 180d)) / Math.PI * 180d, point.X);
|
||||
var location = new Location(Math.Atan(Math.Sinh(point.Y * Math.PI / 180d)) / Math.PI * 180d, point.X);
|
||||
location.Y = point.Y;
|
||||
return location;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,40 +1,21 @@
|
|||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Windows;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("Map Control")]
|
||||
[assembly: AssemblyDescription("WPF Map Control")]
|
||||
#if SILVERLIGHT
|
||||
[assembly: AssemblyTitle("MapControl.Silverlight")]
|
||||
[assembly: AssemblyDescription("XAML Map Control for Silverlight")]
|
||||
#else
|
||||
[assembly: AssemblyTitle("MapControl.WPF")]
|
||||
[assembly: AssemblyDescription("XAML Map Control for WPF")]
|
||||
[assembly: ThemeInfo(ResourceDictionaryLocation.None, ResourceDictionaryLocation.SourceAssembly)]
|
||||
#endif
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("Clemens Fischer")]
|
||||
[assembly: AssemblyProduct("WPF Map Control")]
|
||||
[assembly: AssemblyProduct("XAML Map Control")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2012 Clemens Fischer")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("81bb1cbe-eb23-47d4-a79d-71f425876d02")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
|
||||
// ResourceDictionary locations for default control styles
|
||||
[assembly: ThemeInfo(ResourceDictionaryLocation.None, ResourceDictionaryLocation.SourceAssembly)]
|
||||
[assembly: ComVisible(false)]
|
||||
|
|
|
|||
17
MapControl/Pushpin.WPF.cs
Normal file
17
MapControl/Pushpin.WPF.cs
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
// XAML Map Control - http://xamlmapcontrol.codeplex.com/
|
||||
// Copyright © 2012 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using System.Windows;
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
public partial class Pushpin
|
||||
{
|
||||
static Pushpin()
|
||||
{
|
||||
FrameworkElement.DefaultStyleKeyProperty.OverrideMetadata(
|
||||
typeof(Pushpin), new FrameworkPropertyMetadata(typeof(Pushpin)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,37 +1,55 @@
|
|||
// WPF MapControl - http://wpfmapcontrol.codeplex.com/
|
||||
// XAML Map Control - http://xamlmapcontrol.codeplex.com/
|
||||
// Copyright © 2012 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
#if WINRT
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Data;
|
||||
#else
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
#endif
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
/// <summary>
|
||||
/// Displays a pushpin at the geographic location provided by the Location property.
|
||||
/// Displays a pushpin at a geographic location provided by the MapPanel.Location attached property.
|
||||
/// </summary>
|
||||
[TemplateVisualState(Name = "Normal", GroupName = "CommonStates")]
|
||||
[TemplateVisualState(Name = "MouseOver", GroupName = "CommonStates")]
|
||||
[TemplateVisualState(Name = "Disabled", GroupName = "CommonStates")]
|
||||
public class Pushpin : ContentControl
|
||||
public partial class Pushpin : ContentControl
|
||||
{
|
||||
public static readonly DependencyProperty LocationProperty = MapPanel.LocationProperty.AddOwner(typeof(Pushpin));
|
||||
public static readonly DependencyProperty LocationPathProperty = DependencyProperty.Register(
|
||||
"LocationPath", typeof(string), typeof(Pushpin), new PropertyMetadata(null, LocationPathPropertyChanged));
|
||||
|
||||
static Pushpin()
|
||||
public Pushpin()
|
||||
{
|
||||
FrameworkElement.DefaultStyleKeyProperty.OverrideMetadata(
|
||||
typeof(Pushpin), new FrameworkPropertyMetadata(typeof(Pushpin)));
|
||||
MapPanel.AddParentMapHandlers(this);
|
||||
DefaultStyleKey = typeof(Pushpin);
|
||||
}
|
||||
|
||||
public MapBase ParentMap
|
||||
/// <summary>
|
||||
/// Gets or sets the property path that is used to bind the MapPanel.Location attached property.
|
||||
/// </summary>
|
||||
public string LocationPath
|
||||
{
|
||||
get { return MapPanel.GetParentMap(this); }
|
||||
get { return (string)GetValue(LocationPathProperty); }
|
||||
set { SetValue(LocationPathProperty, value); }
|
||||
}
|
||||
|
||||
public Location Location
|
||||
private static void LocationPathPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
get { return (Location)GetValue(LocationProperty); }
|
||||
set { SetValue(LocationProperty, value); }
|
||||
string path = e.NewValue as string;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(path))
|
||||
{
|
||||
var binding = new Binding
|
||||
{
|
||||
Path = new PropertyPath(path)
|
||||
};
|
||||
|
||||
BindingOperations.SetBinding(obj, MapPanel.LocationProperty, binding);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,14 @@
|
|||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:map="clr-namespace:MapControl">
|
||||
<Style x:Key="{x:Type map:MapItemsControl}" TargetType="{x:Type map:MapItemsControl}">
|
||||
xmlns:map="clr-namespace:MapControl">
|
||||
<Style TargetType="map:MapItemsControl">
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate>
|
||||
<ItemsPresenter/>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Setter Property="ItemsPanel">
|
||||
<Setter.Value>
|
||||
<ItemsPanelTemplate>
|
||||
|
|
@ -10,15 +17,16 @@
|
|||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
<Style x:Key="{x:Type map:MapItem}" TargetType="{x:Type map:MapItem}">
|
||||
<Setter Property="HorizontalAlignment" Value="Left"/>
|
||||
<Setter Property="VerticalAlignment" Value="Top"/>
|
||||
<Style TargetType="map:MapItem">
|
||||
<Setter Property="HorizontalAlignment" Value="Stretch"/>
|
||||
<Setter Property="VerticalAlignment" Value="Stretch"/>
|
||||
<Setter Property="HorizontalContentAlignment" Value="Left"/>
|
||||
<Setter Property="VerticalContentAlignment" Value="Top"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="{x:Type map:MapItem}">
|
||||
<ControlTemplate TargetType="map:MapItem">
|
||||
<ContentPresenter Content="{TemplateBinding Content}"
|
||||
ContentTemplate="{TemplateBinding ContentTemplate}"
|
||||
Cursor="{TemplateBinding Cursor}"
|
||||
Margin="{TemplateBinding Padding}"
|
||||
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
|
||||
|
|
@ -26,40 +34,28 @@
|
|||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
<Style x:Key="{x:Type map:Pushpin}" TargetType="{x:Type map:Pushpin}">
|
||||
<Setter Property="SnapsToDevicePixels" Value="True"/>
|
||||
<Style TargetType="map:Pushpin">
|
||||
<Setter Property="HorizontalAlignment" Value="Left"/>
|
||||
<Setter Property="VerticalAlignment" Value="Bottom"/>
|
||||
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
|
||||
<Setter Property="VerticalContentAlignment" Value="Stretch"/>
|
||||
<Setter Property="Padding" Value="3"/>
|
||||
<Setter Property="Background" Value="White"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="{x:Type map:Pushpin}">
|
||||
<ControlTemplate TargetType="map:Pushpin">
|
||||
<Grid>
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="CommonStates">
|
||||
<VisualState x:Name="Normal"/>
|
||||
<VisualState x:Name="Disabled">
|
||||
<Storyboard>
|
||||
<DoubleAnimation Storyboard.TargetName="contentPresenter" Storyboard.TargetProperty="Opacity" Duration="0" To=".55"/>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="MouseOver">
|
||||
<Storyboard>
|
||||
<DoubleAnimation Storyboard.TargetName="mouseOverRect" Storyboard.TargetProperty="Opacity" Duration="0" To=".35"/>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Path Grid.Row="1" Margin="0,-1,0,0" Fill="{TemplateBinding Background}" Data="M 0,0 L 0,16 16,0"/>
|
||||
<Rectangle Fill="{TemplateBinding Background}"/>
|
||||
<ContentPresenter x:Name="contentPresenter" Margin="{TemplateBinding Padding}"
|
||||
Content="{TemplateBinding Content}" ContentTemplate="{TemplateBinding ContentTemplate}"/>
|
||||
<Rectangle x:Name="mouseOverRect" Margin="{TemplateBinding Padding}" Fill="White" Opacity="0"/>
|
||||
<Path Grid.Row="1" Fill="{TemplateBinding Background}" Data="M 0,-0.5 L 0,16 16,-0.5"/>
|
||||
<ContentPresenter Content="{TemplateBinding Content}"
|
||||
ContentTemplate="{TemplateBinding ContentTemplate}"
|
||||
Margin="{TemplateBinding Padding}"
|
||||
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
|
||||
</Grid>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
|
|
|
|||
71
MapControl/Tile.Silverlight.WinRT.cs
Normal file
71
MapControl/Tile.Silverlight.WinRT.cs
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
// XAML Map Control - http://xamlmapcontrol.codeplex.com/
|
||||
// Copyright © 2012 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
|
||||
#if WINRT
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Media;
|
||||
using Windows.UI.Xaml.Media.Imaging;
|
||||
#else
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
#endif
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
internal partial class Tile
|
||||
{
|
||||
public readonly Image Image = new Image { Stretch = Stretch.Uniform, Opacity = 0d };
|
||||
|
||||
public ImageSource ImageSource
|
||||
{
|
||||
get { return Image.Source; }
|
||||
private set { Image.Source = value; }
|
||||
}
|
||||
|
||||
public void SetImageSource(ImageSource source, bool animateOpacity)
|
||||
{
|
||||
if (ImageSource == null)
|
||||
{
|
||||
if (animateOpacity)
|
||||
{
|
||||
var bitmap = source as BitmapImage;
|
||||
|
||||
if (bitmap != null) // TODO Check if bitmap is downloading somehow, maybe PixelWidth == 0?
|
||||
{
|
||||
bitmap.ImageOpened += BitmapImageOpened;
|
||||
bitmap.ImageFailed += BitmapImageFailed;
|
||||
}
|
||||
else
|
||||
{
|
||||
Image.BeginAnimation(Image.OpacityProperty, OpacityAnimation);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Image.Opacity = 1d;
|
||||
}
|
||||
}
|
||||
|
||||
ImageSource = source;
|
||||
}
|
||||
|
||||
private void BitmapImageOpened(object sender, RoutedEventArgs e)
|
||||
{
|
||||
((BitmapImage)sender).ImageOpened -= BitmapImageOpened;
|
||||
((BitmapImage)sender).ImageFailed -= BitmapImageFailed;
|
||||
Image.BeginAnimation(Image.OpacityProperty, OpacityAnimation);
|
||||
}
|
||||
|
||||
private void BitmapImageFailed(object sender, ExceptionRoutedEventArgs e)
|
||||
{
|
||||
((BitmapImage)sender).ImageOpened -= BitmapImageOpened;
|
||||
((BitmapImage)sender).ImageFailed -= BitmapImageFailed;
|
||||
ImageSource = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
62
MapControl/Tile.WPF.cs
Normal file
62
MapControl/Tile.WPF.cs
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
// XAML Map Control - http://xamlmapcontrol.codeplex.com/
|
||||
// Copyright © 2012 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using System;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
internal partial class Tile
|
||||
{
|
||||
public readonly ImageBrush Brush = new ImageBrush { Opacity = 0d };
|
||||
|
||||
public ImageSource ImageSource
|
||||
{
|
||||
get { return Brush.ImageSource; }
|
||||
private set { Brush.ImageSource = value; }
|
||||
}
|
||||
|
||||
public void SetImageSource(ImageSource source, bool animateOpacity)
|
||||
{
|
||||
if (ImageSource == null)
|
||||
{
|
||||
if (animateOpacity)
|
||||
{
|
||||
var bitmap = source as BitmapImage;
|
||||
|
||||
if (bitmap != null && bitmap.IsDownloading)
|
||||
{
|
||||
bitmap.DownloadCompleted += BitmapDownloadCompleted;
|
||||
bitmap.DownloadFailed += BitmapDownloadFailed;
|
||||
}
|
||||
else
|
||||
{
|
||||
Brush.BeginAnimation(ImageBrush.OpacityProperty, OpacityAnimation);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Brush.Opacity = 1d;
|
||||
}
|
||||
}
|
||||
|
||||
ImageSource = source;
|
||||
}
|
||||
|
||||
private void BitmapDownloadCompleted(object sender, EventArgs e)
|
||||
{
|
||||
((BitmapImage)sender).DownloadCompleted -= BitmapDownloadCompleted;
|
||||
((BitmapImage)sender).DownloadFailed -= BitmapDownloadFailed;
|
||||
Brush.BeginAnimation(ImageBrush.OpacityProperty, OpacityAnimation);
|
||||
}
|
||||
|
||||
private void BitmapDownloadFailed(object sender, ExceptionEventArgs e)
|
||||
{
|
||||
((BitmapImage)sender).DownloadCompleted -= BitmapDownloadCompleted;
|
||||
((BitmapImage)sender).DownloadFailed -= BitmapDownloadFailed;
|
||||
ImageSource = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,22 +1,21 @@
|
|||
// WPF MapControl - http://wpfmapcontrol.codeplex.com/
|
||||
// XAML Map Control - http://xamlmapcontrol.codeplex.com/
|
||||
// Copyright © 2012 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using System;
|
||||
using System.Windows.Media;
|
||||
#if WINRT
|
||||
using Windows.UI.Xaml.Media.Animation;
|
||||
#else
|
||||
using System.Windows.Media.Animation;
|
||||
using System.Windows.Media.Imaging;
|
||||
#endif
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
internal class Tile
|
||||
internal partial class Tile
|
||||
{
|
||||
private static readonly DoubleAnimation opacityAnimation = new DoubleAnimation(0d, 1d, TimeSpan.FromSeconds(0.5), FillBehavior.Stop);
|
||||
|
||||
public readonly int ZoomLevel;
|
||||
public readonly int X;
|
||||
public readonly int Y;
|
||||
public readonly ImageBrush Brush = new ImageBrush();
|
||||
|
||||
public Tile(int zoomLevel, int x, int y)
|
||||
{
|
||||
|
|
@ -31,49 +30,22 @@ namespace MapControl
|
|||
{
|
||||
get
|
||||
{
|
||||
int numTiles = 1 << ZoomLevel;
|
||||
var numTiles = 1 << ZoomLevel;
|
||||
return ((X % numTiles) + numTiles) % numTiles;
|
||||
}
|
||||
}
|
||||
|
||||
public ImageSource Source
|
||||
DoubleAnimation OpacityAnimation
|
||||
{
|
||||
get { return Brush.ImageSource; }
|
||||
set { Brush.ImageSource = value; }
|
||||
}
|
||||
|
||||
public void SetSource(ImageSource source)
|
||||
{
|
||||
if (Source == null)
|
||||
get
|
||||
{
|
||||
BitmapImage bitmap = source as BitmapImage;
|
||||
|
||||
if (bitmap != null && bitmap.IsDownloading)
|
||||
return new DoubleAnimation
|
||||
{
|
||||
bitmap.DownloadCompleted += BitmapDownloadCompleted;
|
||||
bitmap.DownloadFailed += BitmapDownloadFailed;
|
||||
}
|
||||
else
|
||||
{
|
||||
Brush.BeginAnimation(ImageBrush.OpacityProperty, opacityAnimation);
|
||||
}
|
||||
To = 1d,
|
||||
Duration = TimeSpan.FromSeconds(0.5),
|
||||
FillBehavior = FillBehavior.HoldEnd,
|
||||
};
|
||||
}
|
||||
|
||||
Source = source;
|
||||
}
|
||||
|
||||
private void BitmapDownloadCompleted(object sender, EventArgs e)
|
||||
{
|
||||
((BitmapImage)sender).DownloadCompleted -= BitmapDownloadCompleted;
|
||||
((BitmapImage)sender).DownloadFailed -= BitmapDownloadFailed;
|
||||
Brush.BeginAnimation(ImageBrush.OpacityProperty, opacityAnimation);
|
||||
}
|
||||
|
||||
private void BitmapDownloadFailed(object sender, ExceptionEventArgs e)
|
||||
{
|
||||
((BitmapImage)sender).DownloadCompleted -= BitmapDownloadCompleted;
|
||||
((BitmapImage)sender).DownloadFailed -= BitmapDownloadFailed;
|
||||
Source = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
56
MapControl/TileContainer.Silverlight.WinRT.cs
Normal file
56
MapControl/TileContainer.Silverlight.WinRT.cs
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
// XAML Map Control - http://xamlmapcontrol.codeplex.com/
|
||||
// Copyright © 2012 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
#if WINRT
|
||||
using Windows.Foundation;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Media;
|
||||
#else
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media;
|
||||
#endif
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
internal partial class TileContainer : Panel
|
||||
{
|
||||
private Matrix GetTransformMatrix(Matrix transform, double scale)
|
||||
{
|
||||
return transform
|
||||
.Scale(scale, scale)
|
||||
.Translate(offset.X, offset.Y)
|
||||
.RotateAt(rotation, origin.X, origin.Y);
|
||||
}
|
||||
|
||||
private Matrix GetTileIndexMatrix(int numTiles)
|
||||
{
|
||||
var mapToTileScale = (double)numTiles / 360d;
|
||||
return ViewportTransform.Matrix
|
||||
.Invert() // view to map coordinates
|
||||
.Translate(180d, -180d)
|
||||
.Scale(mapToTileScale, -mapToTileScale); // map coordinates to tile indices
|
||||
}
|
||||
|
||||
protected override Size MeasureOverride(Size availableSize)
|
||||
{
|
||||
foreach (TileLayer tileLayer in Children)
|
||||
{
|
||||
tileLayer.Measure(availableSize);
|
||||
}
|
||||
|
||||
return new Size();
|
||||
}
|
||||
|
||||
protected override Size ArrangeOverride(Size finalSize)
|
||||
{
|
||||
foreach (TileLayer tileLayer in Children)
|
||||
{
|
||||
tileLayer.Arrange(new Rect());
|
||||
}
|
||||
|
||||
return finalSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
29
MapControl/TileContainer.WPF.cs
Normal file
29
MapControl/TileContainer.WPF.cs
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
// XAML Map Control - http://xamlmapcontrol.codeplex.com/
|
||||
// Copyright © 2012 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
internal partial class TileContainer : ContainerVisual
|
||||
{
|
||||
private Matrix GetTransformMatrix(Matrix transform, double scale)
|
||||
{
|
||||
transform.Scale(scale, scale);
|
||||
transform.Translate(offset.X, offset.Y);
|
||||
transform.RotateAt(rotation, origin.X, origin.Y);
|
||||
return transform;
|
||||
}
|
||||
|
||||
private Matrix GetTileIndexMatrix(int numTiles)
|
||||
{
|
||||
var mapToTileScale = (double)numTiles / 360d;
|
||||
var transform = ViewportTransform.Matrix;
|
||||
transform.Invert(); // view to map coordinates
|
||||
transform.Translate(180d, -180d);
|
||||
transform.Scale(mapToTileScale, -mapToTileScale); // map coordinates to tile indices
|
||||
return transform;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,23 +1,29 @@
|
|||
// WPF MapControl - http://wpfmapcontrol.codeplex.com/
|
||||
// XAML Map Control - http://xamlmapcontrol.codeplex.com/
|
||||
// Copyright © 2012 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
#if WINRT
|
||||
using Windows.Foundation;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Media;
|
||||
#else
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Threading;
|
||||
#endif
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
internal class TileContainer : ContainerVisual
|
||||
internal partial class TileContainer
|
||||
{
|
||||
private const double maxScaledTileSize = 400d; // scaled tile size 200..400 units
|
||||
private static double zoomLevelSwitchOffset = Math.Log(maxScaledTileSize / 256d, 2d);
|
||||
|
||||
private Size size;
|
||||
private Point origin;
|
||||
private Vector offset;
|
||||
private Point offset;
|
||||
private double rotation;
|
||||
private double zoomLevel;
|
||||
private int tileZoomLevel;
|
||||
|
|
@ -28,37 +34,38 @@ namespace MapControl
|
|||
|
||||
public TileContainer()
|
||||
{
|
||||
updateTimer = new DispatcherTimer(TimeSpan.FromSeconds(0.5), DispatcherPriority.Background, UpdateTiles, Dispatcher);
|
||||
updateTimer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(0.5) };
|
||||
updateTimer.Tick += UpdateTiles;
|
||||
}
|
||||
|
||||
public void AddTileLayers(int index, IEnumerable<TileLayer> tileLayers)
|
||||
{
|
||||
Matrix tileLayerTransform = GetTileLayerTransform();
|
||||
var tileLayerTransform = GetTileLayerTransformMatrix();
|
||||
|
||||
foreach (TileLayer tileLayer in tileLayers)
|
||||
foreach (var tileLayer in tileLayers)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(tileLayer.SourceName))
|
||||
if (index < Children.Count)
|
||||
{
|
||||
throw new ArgumentException("TileLayer.SourceName property must not be null or empty or white-space only.");
|
||||
Children.Insert(index, tileLayer);
|
||||
}
|
||||
else
|
||||
{
|
||||
Children.Add(tileLayer);
|
||||
}
|
||||
|
||||
Children.Insert(index++, tileLayer);
|
||||
index++;
|
||||
tileLayer.TransformMatrix = tileLayerTransform;
|
||||
tileLayer.UpdateTiles(tileZoomLevel, tileGrid);
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveTileLayers(int index, IEnumerable<TileLayer> tileLayers)
|
||||
public void RemoveTileLayers(int index, int count)
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
foreach (TileLayer tileLayer in tileLayers)
|
||||
while (count-- > 0)
|
||||
{
|
||||
tileLayer.ClearTiles();
|
||||
count++;
|
||||
((TileLayer)Children[index]).ClearTiles();
|
||||
Children.RemoveAt(index);
|
||||
}
|
||||
|
||||
Children.RemoveRange(index, count);
|
||||
}
|
||||
|
||||
public void ClearTileLayers()
|
||||
|
|
@ -73,8 +80,8 @@ namespace MapControl
|
|||
|
||||
public double SetViewportTransform(double mapZoomLevel, double mapRotation, Point mapOrigin, Point viewportOrigin, Size viewportSize)
|
||||
{
|
||||
double scale = Math.Pow(2d, zoomLevel) * 256d / 360d;
|
||||
double oldMapOriginX = (origin.X - offset.X) / scale - 180d;
|
||||
var scale = Math.Pow(2d, zoomLevel) * 256d / 360d;
|
||||
var oldMapOriginX = (origin.X - offset.X) / scale - 180d;
|
||||
|
||||
if (zoomLevel != mapZoomLevel)
|
||||
{
|
||||
|
|
@ -89,13 +96,9 @@ namespace MapControl
|
|||
offset.X = origin.X - (180d + mapOrigin.X) * scale;
|
||||
offset.Y = origin.Y - (180d - mapOrigin.Y) * scale;
|
||||
|
||||
Matrix transform = new Matrix(1d, 0d, 0d, -1d, 180d, 180d);
|
||||
transform.Scale(scale, scale);
|
||||
transform.Translate(offset.X, offset.Y);
|
||||
transform.RotateAt(rotation, origin.X, origin.Y);
|
||||
ViewportTransform.Matrix = transform;
|
||||
ViewportTransform.Matrix = GetTransformMatrix(new Matrix(1d, 0d, 0d, -1d, 180d, 180d), scale);
|
||||
|
||||
Matrix tileLayerTransform = GetTileLayerTransform();
|
||||
var tileLayerTransform = GetTileLayerTransformMatrix();
|
||||
|
||||
foreach (TileLayer tileLayer in Children)
|
||||
{
|
||||
|
|
@ -104,7 +107,7 @@ namespace MapControl
|
|||
|
||||
if (Math.Sign(mapOrigin.X) == Math.Sign(oldMapOriginX))
|
||||
{
|
||||
updateTimer.IsEnabled = true;
|
||||
updateTimer.Start();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -115,55 +118,47 @@ namespace MapControl
|
|||
return scale;
|
||||
}
|
||||
|
||||
private Matrix GetTileLayerTransform()
|
||||
private Matrix GetTileLayerTransformMatrix()
|
||||
{
|
||||
// Calculates the transform matrix that enables rendering of 256x256 tile rectangles in
|
||||
// TileLayer.UpdateTiles with origin at tileGrid.X and tileGrid.Y to minimize rounding errors.
|
||||
// Calculates the TileLayer VisualTransform or RenderTransform matrix
|
||||
// with origin at tileGrid.X and tileGrid.Y to minimize rounding errors.
|
||||
|
||||
double scale = Math.Pow(2d, zoomLevel - tileZoomLevel);
|
||||
Matrix transform = new Matrix(1d, 0d, 0d, 1d, tileGrid.X * 256d, tileGrid.Y * 256d);
|
||||
transform.Scale(scale, scale);
|
||||
transform.Translate(offset.X, offset.Y);
|
||||
transform.RotateAt(rotation, origin.X, origin.Y);
|
||||
|
||||
return transform;
|
||||
return GetTransformMatrix(
|
||||
new Matrix(1d, 0d, 0d, 1d, tileGrid.X * 256d, tileGrid.Y * 256d),
|
||||
Math.Pow(2d, zoomLevel - tileZoomLevel));
|
||||
}
|
||||
|
||||
private void UpdateTiles(object sender, EventArgs e)
|
||||
private void UpdateTiles(object sender, object e)
|
||||
{
|
||||
updateTimer.IsEnabled = false;
|
||||
updateTimer.Stop();
|
||||
|
||||
int zoom = (int)Math.Floor(zoomLevel + 1d - zoomLevelSwitchOffset);
|
||||
int numTiles = 1 << zoom;
|
||||
double mapToTileScale = (double)numTiles / 360d;
|
||||
Matrix transform = ViewportTransform.Matrix;
|
||||
transform.Invert(); // view to map coordinates
|
||||
transform.Translate(180d, -180d);
|
||||
transform.Scale(mapToTileScale, -mapToTileScale); // map coordinates to tile indices
|
||||
var zoom = (int)Math.Floor(zoomLevel + 1d - zoomLevelSwitchOffset);
|
||||
var numTiles = 1 << zoom;
|
||||
var transform = GetTileIndexMatrix(numTiles);
|
||||
|
||||
// tile indices of visible rectangle
|
||||
Point p1 = transform.Transform(new Point(0d, 0d));
|
||||
Point p2 = transform.Transform(new Point(size.Width, 0d));
|
||||
Point p3 = transform.Transform(new Point(0d, size.Height));
|
||||
Point p4 = transform.Transform(new Point(size.Width, size.Height));
|
||||
var p1 = transform.Transform(new Point(0d, 0d));
|
||||
var p2 = transform.Transform(new Point(size.Width, 0d));
|
||||
var p3 = transform.Transform(new Point(0d, size.Height));
|
||||
var p4 = transform.Transform(new Point(size.Width, size.Height));
|
||||
|
||||
double left = Math.Min(p1.X, Math.Min(p2.X, Math.Min(p3.X, p4.X)));
|
||||
double right = Math.Max(p1.X, Math.Max(p2.X, Math.Max(p3.X, p4.X)));
|
||||
double top = Math.Min(p1.Y, Math.Min(p2.Y, Math.Min(p3.Y, p4.Y)));
|
||||
double bottom = Math.Max(p1.Y, Math.Max(p2.Y, Math.Max(p3.Y, p4.Y)));
|
||||
var left = Math.Min(p1.X, Math.Min(p2.X, Math.Min(p3.X, p4.X)));
|
||||
var right = Math.Max(p1.X, Math.Max(p2.X, Math.Max(p3.X, p4.X)));
|
||||
var top = Math.Min(p1.Y, Math.Min(p2.Y, Math.Min(p3.Y, p4.Y)));
|
||||
var bottom = Math.Max(p1.Y, Math.Max(p2.Y, Math.Max(p3.Y, p4.Y)));
|
||||
|
||||
// index ranges of visible tiles
|
||||
int x1 = (int)Math.Floor(left);
|
||||
int x2 = (int)Math.Floor(right);
|
||||
int y1 = Math.Max((int)Math.Floor(top), 0);
|
||||
int y2 = Math.Min((int)Math.Floor(bottom), numTiles - 1);
|
||||
Int32Rect grid = new Int32Rect(x1, y1, x2 - x1 + 1, y2 - y1 + 1);
|
||||
var x1 = (int)Math.Floor(left);
|
||||
var x2 = (int)Math.Floor(right);
|
||||
var y1 = Math.Max((int)Math.Floor(top), 0);
|
||||
var y2 = Math.Min((int)Math.Floor(bottom), numTiles - 1);
|
||||
var grid = new Int32Rect(x1, y1, x2 - x1 + 1, y2 - y1 + 1);
|
||||
|
||||
if (tileZoomLevel != zoom || tileGrid != grid)
|
||||
{
|
||||
tileZoomLevel = zoom;
|
||||
tileGrid = grid;
|
||||
Matrix tileLayerTransform = GetTileLayerTransform();
|
||||
var tileLayerTransform = GetTileLayerTransformMatrix();
|
||||
|
||||
foreach (TileLayer tileLayer in Children)
|
||||
{
|
||||
|
|
|
|||
40
MapControl/TileImageLoader.Silverlight.WinRT.cs
Normal file
40
MapControl/TileImageLoader.Silverlight.WinRT.cs
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
// XAML Map Control - http://xamlmapcontrol.codeplex.com/
|
||||
// Copyright © 2012 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
#if WINRT
|
||||
using Windows.UI.Xaml.Media.Imaging;
|
||||
#else
|
||||
using System.Windows.Media.Imaging;
|
||||
#endif
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
/// <summary>
|
||||
/// Loads map tile images by their URIs.
|
||||
/// </summary>
|
||||
internal class TileImageLoader
|
||||
{
|
||||
private readonly TileLayer tileLayer;
|
||||
|
||||
internal TileImageLoader(TileLayer tileLayer)
|
||||
{
|
||||
this.tileLayer = tileLayer;
|
||||
}
|
||||
|
||||
internal void BeginGetTiles(IEnumerable<Tile> tiles)
|
||||
{
|
||||
foreach (var tile in tiles.Where(t => t.ImageSource == null && t.Uri == null))
|
||||
{
|
||||
tile.Uri = tileLayer.TileSource.GetUri(tile.XIndex, tile.Y, tile.ZoomLevel);
|
||||
tile.SetImageSource(new BitmapImage(tile.Uri), true);
|
||||
}
|
||||
}
|
||||
|
||||
internal void CancelGetTiles()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
// WPF MapControl - http://wpfmapcontrol.codeplex.com/
|
||||
// XAML Map Control - http://xamlmapcontrol.codeplex.com/
|
||||
// Copyright © 2012 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
|
|
@ -73,7 +73,7 @@ namespace MapControl
|
|||
|
||||
internal void BeginGetTiles(IEnumerable<Tile> tiles)
|
||||
{
|
||||
ThreadPool.QueueUserWorkItem(BeginGetTilesAsync, new List<Tile>(tiles.Where(t => t.Source == null && t.Uri == null)));
|
||||
ThreadPool.QueueUserWorkItem(BeginGetTilesAsync, new List<Tile>(tiles.Where(t => t.ImageSource == null && t.Uri == null)));
|
||||
}
|
||||
|
||||
internal void CancelGetTiles()
|
||||
|
|
@ -84,26 +84,26 @@ namespace MapControl
|
|||
|
||||
private void BeginGetTilesAsync(object newTilesList)
|
||||
{
|
||||
List<Tile> newTiles = (List<Tile>)newTilesList;
|
||||
ImageTileSource imageTileSource = tileLayer.TileSource as ImageTileSource;
|
||||
var newTiles = (List<Tile>)newTilesList;
|
||||
var imageTileSource = tileLayer.TileSource as ImageTileSource;
|
||||
|
||||
if (imageTileSource == null)
|
||||
{
|
||||
if (Cache == null)
|
||||
if (Cache == null || string.IsNullOrWhiteSpace(tileLayer.SourceName))
|
||||
{
|
||||
foreach (Tile tile in newTiles)
|
||||
foreach (var tile in newTiles)
|
||||
{
|
||||
pendingTiles.Enqueue(tile);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
List<Tile> outdatedTiles = new List<Tile>(newTiles.Count);
|
||||
var outdatedTiles = new List<Tile>(newTiles.Count);
|
||||
|
||||
foreach (Tile tile in newTiles)
|
||||
foreach (var tile in newTiles)
|
||||
{
|
||||
string key = CacheKey(tile);
|
||||
byte[] buffer = Cache.Get(key) as byte[];
|
||||
var key = CacheKey(tile);
|
||||
var buffer = Cache.Get(key) as byte[];
|
||||
|
||||
if (buffer == null)
|
||||
{
|
||||
|
|
@ -122,7 +122,7 @@ namespace MapControl
|
|||
}
|
||||
}
|
||||
|
||||
foreach (Tile tile in outdatedTiles)
|
||||
foreach (var tile in outdatedTiles)
|
||||
{
|
||||
pendingTiles.Enqueue(tile);
|
||||
}
|
||||
|
|
@ -137,18 +137,22 @@ namespace MapControl
|
|||
}
|
||||
else if (imageTileSource.CanLoadAsync)
|
||||
{
|
||||
foreach (Tile tile in newTiles)
|
||||
foreach (var tile in newTiles)
|
||||
{
|
||||
tileLayer.Dispatcher.BeginInvoke(DispatcherPriority.Background,
|
||||
(Action<Tile, ImageSource>)((t, s) => t.SetSource(s)), tile, imageTileSource.LoadImage(tile.XIndex, tile.Y, tile.ZoomLevel));
|
||||
tileLayer.Dispatcher.BeginInvoke(
|
||||
(Action<Tile, ImageSource>)((t, s) => t.SetImageSource(s, true)),
|
||||
DispatcherPriority.Background,
|
||||
tile, imageTileSource.LoadImage(tile.XIndex, tile.Y, tile.ZoomLevel));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (Tile tile in newTiles)
|
||||
foreach (var tile in newTiles)
|
||||
{
|
||||
tileLayer.Dispatcher.BeginInvoke(DispatcherPriority.Background,
|
||||
(Action<Tile>)(t => t.SetSource(imageTileSource.LoadImage(t.XIndex, t.Y, t.ZoomLevel))), tile);
|
||||
tileLayer.Dispatcher.BeginInvoke(
|
||||
(Action<Tile>)(t => t.SetImageSource(imageTileSource.LoadImage(t.XIndex, t.Y, t.ZoomLevel), true)),
|
||||
DispatcherPriority.Background,
|
||||
tile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -159,7 +163,7 @@ namespace MapControl
|
|||
while (pendingTiles.TryDequeue(out tile))
|
||||
{
|
||||
tile.Uri = tileLayer.TileSource.GetUri(tile.XIndex, tile.Y, tile.ZoomLevel);
|
||||
byte[] buffer = DownloadImage(tile.Uri);
|
||||
var buffer = DownloadImage(tile.Uri);
|
||||
|
||||
if (buffer != null && CreateTileImage(tile, buffer) && Cache != null)
|
||||
{
|
||||
|
|
@ -177,11 +181,11 @@ namespace MapControl
|
|||
|
||||
private bool CreateTileImage(Tile tile, byte[] buffer)
|
||||
{
|
||||
BitmapImage bitmap = new BitmapImage();
|
||||
var bitmap = new BitmapImage();
|
||||
|
||||
try
|
||||
{
|
||||
using (Stream stream = new MemoryStream(buffer, 8, buffer.Length - 8, false))
|
||||
using (var stream = new MemoryStream(buffer, 8, buffer.Length - 8, false))
|
||||
{
|
||||
bitmap.BeginInit();
|
||||
bitmap.CacheOption = BitmapCacheOption.OnLoad;
|
||||
|
|
@ -196,7 +200,11 @@ namespace MapControl
|
|||
return false;
|
||||
}
|
||||
|
||||
tileLayer.Dispatcher.BeginInvoke(DispatcherPriority.Background, (Action<Tile>)(t => t.SetSource(bitmap)), tile);
|
||||
tileLayer.Dispatcher.BeginInvoke(
|
||||
(Action<Tile>)(t => t.SetImageSource(bitmap, true)),
|
||||
DispatcherPriority.Background,
|
||||
tile);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -206,16 +214,16 @@ namespace MapControl
|
|||
|
||||
try
|
||||
{
|
||||
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
|
||||
var request = (HttpWebRequest)WebRequest.Create(uri);
|
||||
request.UserAgent = typeof(TileImageLoader).ToString();
|
||||
|
||||
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
|
||||
using (Stream responseStream = response.GetResponseStream())
|
||||
{
|
||||
long length = response.ContentLength;
|
||||
long creationTime = DateTime.UtcNow.ToBinary();
|
||||
var length = response.ContentLength;
|
||||
var creationTime = DateTime.UtcNow.ToBinary();
|
||||
|
||||
using (MemoryStream memoryStream = length > 0 ? new MemoryStream((int)length + 8) : new MemoryStream())
|
||||
using (var memoryStream = length > 0 ? new MemoryStream((int)length + 8) : new MemoryStream())
|
||||
{
|
||||
memoryStream.Write(BitConverter.GetBytes(creationTime), 0, 8);
|
||||
responseStream.CopyTo(memoryStream);
|
||||
|
|
@ -230,7 +238,7 @@ namespace MapControl
|
|||
{
|
||||
if (ex.Status == WebExceptionStatus.ProtocolError)
|
||||
{
|
||||
HttpStatusCode statusCode = ((HttpWebResponse)ex.Response).StatusCode;
|
||||
var statusCode = ((HttpWebResponse)ex.Response).StatusCode;
|
||||
if (statusCode != HttpStatusCode.NotFound)
|
||||
{
|
||||
Trace.TraceInformation("Downloading {0} failed: {1}", uri, ex.Message);
|
||||
|
|
@ -251,7 +259,7 @@ namespace MapControl
|
|||
|
||||
private static bool IsCacheOutdated(byte[] buffer)
|
||||
{
|
||||
long creationTime = BitConverter.ToInt64(buffer, 0);
|
||||
var creationTime = BitConverter.ToInt64(buffer, 0);
|
||||
|
||||
return DateTime.FromBinary(creationTime) + CacheUpdateAge < DateTime.UtcNow;
|
||||
}
|
||||
59
MapControl/TileLayer.Silverlight.WinRT.cs
Normal file
59
MapControl/TileLayer.Silverlight.WinRT.cs
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
// XAML Map Control - http://xamlmapcontrol.codeplex.com/
|
||||
// Copyright © 2012 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
#if WINRT
|
||||
using Windows.Foundation;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
#else
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
#endif
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
public partial class TileLayer : Panel
|
||||
{
|
||||
partial void Initialize()
|
||||
{
|
||||
RenderTransform = transform;
|
||||
}
|
||||
|
||||
private Panel TileContainer
|
||||
{
|
||||
get { return Parent as Panel; }
|
||||
}
|
||||
|
||||
private void RenderTiles()
|
||||
{
|
||||
Children.Clear();
|
||||
foreach (var tile in tiles)
|
||||
{
|
||||
Children.Add(tile.Image);
|
||||
}
|
||||
}
|
||||
|
||||
protected override Size MeasureOverride(Size availableSize)
|
||||
{
|
||||
foreach (var tile in tiles)
|
||||
{
|
||||
tile.Image.Measure(availableSize);
|
||||
}
|
||||
|
||||
return new Size();
|
||||
}
|
||||
|
||||
protected override Size ArrangeOverride(Size finalSize)
|
||||
{
|
||||
foreach (var tile in tiles)
|
||||
{
|
||||
var tileSize = (double)(256 << (zoomLevel - tile.ZoomLevel));
|
||||
tile.Image.Width = tileSize;
|
||||
tile.Image.Height = tileSize;
|
||||
tile.Image.Arrange(new Rect(tileSize * tile.X - 256 * grid.X, tileSize * tile.Y - 256 * grid.Y, tileSize, tileSize));
|
||||
}
|
||||
|
||||
return finalSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
43
MapControl/TileLayer.WPF.cs
Normal file
43
MapControl/TileLayer.WPF.cs
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
// XAML Map Control - http://xamlmapcontrol.codeplex.com/
|
||||
// Copyright © 2012 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
public partial class TileLayer : DrawingVisual
|
||||
{
|
||||
partial void Initialize()
|
||||
{
|
||||
VisualTransform = transform;
|
||||
VisualEdgeMode = EdgeMode.Aliased;
|
||||
}
|
||||
|
||||
private ContainerVisual TileContainer
|
||||
{
|
||||
get { return Parent as ContainerVisual; }
|
||||
}
|
||||
|
||||
private void RenderTiles()
|
||||
{
|
||||
//System.Diagnostics.Trace.TraceInformation("{0} Tiles: {1}", tiles.Count, string.Join(", ", tiles.Select(t => t.ZoomLevel.ToString())));
|
||||
|
||||
using (var drawingContext = RenderOpen())
|
||||
{
|
||||
foreach (var tile in tiles)
|
||||
{
|
||||
var tileSize = 256 << (zoomLevel - tile.ZoomLevel);
|
||||
var tileRect = new Rect(tileSize * tile.X - 256 * grid.X, tileSize * tile.Y - 256 * grid.Y, tileSize, tileSize);
|
||||
|
||||
drawingContext.DrawRectangle(tile.Brush, null, tileRect);
|
||||
|
||||
//if (tile.ZoomLevel == zoomLevel)
|
||||
// drawingContext.DrawText(new FormattedText(string.Format("{0}-{1}-{2}", tile.ZoomLevel, tile.X, tile.Y),
|
||||
// System.Globalization.CultureInfo.InvariantCulture, FlowDirection.LeftToRight, new Typeface("Segoe UI"), 14, Brushes.Black), tileRect.TopLeft);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,21 +1,45 @@
|
|||
// WPF MapControl - http://wpfmapcontrol.codeplex.com/
|
||||
// XAML Map Control - http://xamlmapcontrol.codeplex.com/
|
||||
// Copyright © 2012 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
#if WINRT
|
||||
using Windows.UI.Xaml.Markup;
|
||||
using Windows.UI.Xaml.Media;
|
||||
#else
|
||||
using System.Windows;
|
||||
using System.Windows.Markup;
|
||||
using System.Windows.Media;
|
||||
#endif
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
/// <summary>
|
||||
/// Fills a rectangular area with map tiles from a TileSource.
|
||||
/// </summary>
|
||||
#if WINRT
|
||||
[ContentProperty(Name = "TileSource")]
|
||||
#else
|
||||
[ContentProperty("TileSource")]
|
||||
public class TileLayer : DrawingVisual
|
||||
#endif
|
||||
public partial class TileLayer
|
||||
{
|
||||
public static TileLayer Default
|
||||
{
|
||||
get
|
||||
{
|
||||
return new TileLayer
|
||||
{
|
||||
SourceName = "OpenStreetMap",
|
||||
Description = "© {y} OpenStreetMap Contributors, CC-BY-SA",
|
||||
TileSource = new TileSource("http://{c}.tile.openstreetmap.org/{z}/{x}/{y}.png")
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private readonly MatrixTransform transform = new MatrixTransform();
|
||||
private readonly TileImageLoader tileImageLoader;
|
||||
private List<Tile> tiles = new List<Tile>();
|
||||
private string description = string.Empty;
|
||||
|
|
@ -25,13 +49,14 @@ namespace MapControl
|
|||
public TileLayer()
|
||||
{
|
||||
tileImageLoader = new TileImageLoader(this);
|
||||
VisualTransform = new MatrixTransform();
|
||||
VisualEdgeMode = EdgeMode.Aliased;
|
||||
MinZoomLevel = 1;
|
||||
MaxZoomLevel = 18;
|
||||
MaxParallelDownloads = 8;
|
||||
Initialize();
|
||||
}
|
||||
|
||||
partial void Initialize();
|
||||
|
||||
public string SourceName { get; set; }
|
||||
public TileSource TileSource { get; set; }
|
||||
public int MinZoomLevel { get; set; }
|
||||
|
|
@ -45,10 +70,16 @@ namespace MapControl
|
|||
set { description = value.Replace("{y}", DateTime.Now.Year.ToString()); }
|
||||
}
|
||||
|
||||
public Matrix TransformMatrix
|
||||
public string TileSourceUriFormat
|
||||
{
|
||||
get { return ((MatrixTransform)VisualTransform).Matrix; }
|
||||
set { ((MatrixTransform)VisualTransform).Matrix = value; }
|
||||
get { return TileSource != null ? TileSource.UriFormat : string.Empty; }
|
||||
set { TileSource = new TileSource(value); }
|
||||
}
|
||||
|
||||
internal Matrix TransformMatrix
|
||||
{
|
||||
get { return transform.Matrix; }
|
||||
set { transform.Matrix = value; }
|
||||
}
|
||||
|
||||
internal void UpdateTiles(int zoomLevel, Int32Rect grid)
|
||||
|
|
@ -75,39 +106,41 @@ namespace MapControl
|
|||
|
||||
private void SelectTiles()
|
||||
{
|
||||
int maxZoomLevel = Math.Min(zoomLevel, MaxZoomLevel);
|
||||
int minZoomLevel = maxZoomLevel;
|
||||
ContainerVisual parent = Parent as ContainerVisual;
|
||||
var maxZoomLevel = Math.Min(zoomLevel, MaxZoomLevel);
|
||||
var minZoomLevel = maxZoomLevel;
|
||||
var container = TileContainer;
|
||||
|
||||
if (parent != null && parent.Children.IndexOf(this) == 0)
|
||||
if (container != null && container.Children.IndexOf(this) == 0)
|
||||
{
|
||||
minZoomLevel = MinZoomLevel;
|
||||
}
|
||||
|
||||
List<Tile> newTiles = new List<Tile>();
|
||||
var newTiles = new List<Tile>();
|
||||
|
||||
for (int z = minZoomLevel; z <= maxZoomLevel; z++)
|
||||
for (var z = minZoomLevel; z <= maxZoomLevel; z++)
|
||||
{
|
||||
int tileSize = 1 << (zoomLevel - z);
|
||||
int x1 = grid.X / tileSize;
|
||||
int x2 = (grid.X + grid.Width - 1) / tileSize;
|
||||
int y1 = Math.Max(0, grid.Y / tileSize);
|
||||
int y2 = Math.Min((1 << z) - 1, (grid.Y + grid.Height - 1) / tileSize);
|
||||
var tileSize = 1 << (zoomLevel - z);
|
||||
var x1 = grid.X / tileSize;
|
||||
var x2 = (grid.X + grid.Width - 1) / tileSize;
|
||||
var y1 = Math.Max(0, grid.Y / tileSize);
|
||||
var y2 = Math.Min((1 << z) - 1, (grid.Y + grid.Height - 1) / tileSize);
|
||||
|
||||
for (int y = y1; y <= y2; y++)
|
||||
for (var y = y1; y <= y2; y++)
|
||||
{
|
||||
for (int x = x1; x <= x2; x++)
|
||||
for (var x = x1; x <= x2; x++)
|
||||
{
|
||||
Tile tile = tiles.Find(t => t.ZoomLevel == z && t.X == x && t.Y == y);
|
||||
var tile = tiles.FirstOrDefault(t => t.ZoomLevel == z && t.X == x && t.Y == y);
|
||||
|
||||
if (tile == null)
|
||||
{
|
||||
tile = new Tile(z, x, y);
|
||||
Tile equivalent = tiles.Find(t => t.Source != null && t.ZoomLevel == z && t.XIndex == tile.XIndex && t.Y == y);
|
||||
|
||||
if (equivalent != null)
|
||||
var equivalentTile = tiles.FirstOrDefault(t => t.ImageSource != null && t.ZoomLevel == z && t.XIndex == tile.XIndex && t.Y == y);
|
||||
|
||||
if (equivalentTile != null)
|
||||
{
|
||||
tile.Source = equivalent.Source;
|
||||
// do not animate to avoid flicker when crossing date line
|
||||
tile.SetImageSource(equivalentTile.ImageSource, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -117,25 +150,7 @@ namespace MapControl
|
|||
}
|
||||
|
||||
tiles = newTiles;
|
||||
//System.Diagnostics.Trace.TraceInformation("{0} Tiles: {1}", tiles.Count, string.Join(", ", tiles.Select(t => t.ZoomLevel.ToString())));
|
||||
}
|
||||
|
||||
private void RenderTiles()
|
||||
{
|
||||
using (DrawingContext drawingContext = RenderOpen())
|
||||
{
|
||||
foreach (Tile tile in tiles)
|
||||
{
|
||||
int tileSize = 256 << (zoomLevel - tile.ZoomLevel);
|
||||
Rect tileRect = new Rect(tileSize * tile.X - 256 * grid.X, tileSize * tile.Y - 256 * grid.Y, tileSize, tileSize);
|
||||
|
||||
drawingContext.DrawRectangle(tile.Brush, null, tileRect);
|
||||
|
||||
//if (tile.ZoomLevel == zoomLevel)
|
||||
// drawingContext.DrawText(new FormattedText(string.Format("{0}-{1}-{2}", tile.ZoomLevel, tile.X, tile.Y),
|
||||
// System.Globalization.CultureInfo.InvariantCulture, FlowDirection.LeftToRight, new Typeface("Segoe UI"), 14, Brushes.Black), tileRect.TopLeft);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,17 @@
|
|||
// WPF MapControl - http://wpfmapcontrol.codeplex.com/
|
||||
// XAML Map Control - http://xamlmapcontrol.codeplex.com/
|
||||
// Copyright © 2012 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
public class TileLayerCollection : ObservableCollection<TileLayer>
|
||||
{
|
||||
public TileLayer this[string sourceName]
|
||||
{
|
||||
get { return this.FirstOrDefault(t => t.SourceName == sourceName); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,20 +1,22 @@
|
|||
// WPF MapControl - http://wpfmapcontrol.codeplex.com/
|
||||
// XAML Map Control - http://xamlmapcontrol.codeplex.com/
|
||||
// Copyright © 2012 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
#if WINRT
|
||||
using Windows.Foundation;
|
||||
#else
|
||||
using System.Windows;
|
||||
#endif
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides the URI of a map tile.
|
||||
/// </summary>
|
||||
[TypeConverter(typeof(TileSourceConverter))]
|
||||
public class TileSource
|
||||
public partial class TileSource
|
||||
{
|
||||
private Func<int, int, int, Uri> getUri;
|
||||
private string uriFormat = string.Empty;
|
||||
|
|
@ -123,9 +125,9 @@ namespace MapControl
|
|||
|
||||
private Uri GetQuadKeyUri(int x, int y, int zoomLevel)
|
||||
{
|
||||
StringBuilder key = new StringBuilder { Length = zoomLevel };
|
||||
var key = new StringBuilder { Length = zoomLevel };
|
||||
|
||||
for (int z = zoomLevel - 1; z >= 0; z--, x /= 2, y /= 2)
|
||||
for (var z = zoomLevel - 1; z >= 0; z--, x /= 2, y /= 2)
|
||||
{
|
||||
key[z] = (char)('0' + 2 * (y % 2) + (x % 2));
|
||||
}
|
||||
|
|
@ -137,14 +139,14 @@ namespace MapControl
|
|||
|
||||
private Uri GetBoundingBoxUri(int x, int y, int zoomLevel)
|
||||
{
|
||||
MercatorTransform t = new MercatorTransform();
|
||||
double n = 1 << zoomLevel;
|
||||
double x1 = (double)x * 360d / n - 180d;
|
||||
double x2 = (double)(x + 1) * 360d / n - 180d;
|
||||
double y1 = 180d - (double)(y + 1) * 360d / n;
|
||||
double y2 = 180d - (double)y * 360d / n;
|
||||
Location p1 = t.TransformBack(new Point(x1, y1));
|
||||
Location p2 = t.TransformBack(new Point(x2, y2));
|
||||
var t = new MercatorTransform();
|
||||
var n = (double)(1 << zoomLevel);
|
||||
var x1 = (double)x * 360d / n - 180d;
|
||||
var x2 = (double)(x + 1) * 360d / n - 180d;
|
||||
var y1 = 180d - (double)(y + 1) * 360d / n;
|
||||
var y2 = 180d - (double)y * 360d / n;
|
||||
var p1 = t.Transform(new Point(x1, y1));
|
||||
var p2 = t.Transform(new Point(x2, y2));
|
||||
|
||||
return new Uri(UriFormat.
|
||||
Replace("{w}", p1.Longitude.ToString(CultureInfo.InvariantCulture)).
|
||||
|
|
@ -153,20 +155,4 @@ namespace MapControl
|
|||
Replace("{n}", p2.Latitude.ToString(CultureInfo.InvariantCulture)));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts from string to TileSource.
|
||||
/// </summary>
|
||||
public class TileSourceConverter : TypeConverter
|
||||
{
|
||||
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
|
||||
{
|
||||
return sourceType == typeof(string);
|
||||
}
|
||||
|
||||
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
|
||||
{
|
||||
return new TileSource(value as string);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
28
MapControl/TileSourceConverter.cs
Normal file
28
MapControl/TileSourceConverter.cs
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
// XAML Map Control - http://xamlmapcontrol.codeplex.com/
|
||||
// Copyright © 2012 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
public class TileSourceConverter : TypeConverter
|
||||
{
|
||||
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
|
||||
{
|
||||
return sourceType == typeof(string);
|
||||
}
|
||||
|
||||
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
|
||||
{
|
||||
return new TileSource(value as string);
|
||||
}
|
||||
}
|
||||
|
||||
[TypeConverter(typeof(TileSourceConverter))]
|
||||
public partial class TileSource
|
||||
{
|
||||
}
|
||||
}
|
||||
17
MapControl/TransformEx.WinRT.cs
Normal file
17
MapControl/TransformEx.WinRT.cs
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
// XAML Map Control - http://xamlmapcontrol.codeplex.com/
|
||||
// Copyright © 2012 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using Windows.Foundation;
|
||||
using Windows.UI.Xaml.Media;
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
public static class TransformEx
|
||||
{
|
||||
public static Point Transform(this GeneralTransform transform, Point point)
|
||||
{
|
||||
return transform.TransformPoint(point);
|
||||
}
|
||||
}
|
||||
}
|
||||
229
MapControl/WinRT/MapControl.WinRT.csproj
Normal file
229
MapControl/WinRT/MapControl.WinRT.csproj
Normal file
|
|
@ -0,0 +1,229 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProductVersion>8.0.30703</ProductVersion>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<ProjectGuid>{63CEFDF7-5170-43B6-86F8-5C4A383A1615}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>MapControl</RootNamespace>
|
||||
<AssemblyName>MapControl.WinRT</AssemblyName>
|
||||
<DefaultLanguage>en-US</DefaultLanguage>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<ProjectTypeGuids>{BC8A1FFA-BEE3-4634-8014-F334798102B3};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>..\bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE;NETFX_CORE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>..\bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE;NETFX_CORE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|ARM'">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<OutputPath>..\bin\ARM\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE;NETFX_CORE</DefineConstants>
|
||||
<NoWarn>;2008</NoWarn>
|
||||
<DebugType>full</DebugType>
|
||||
<PlatformTarget>ARM</PlatformTarget>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|ARM'">
|
||||
<OutputPath>..\bin\ARM\Release\</OutputPath>
|
||||
<DefineConstants>TRACE;NETFX_CORE</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<NoWarn>;2008</NoWarn>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>ARM</PlatformTarget>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<OutputPath>..\bin\x64\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE;NETFX_CORE</DefineConstants>
|
||||
<NoWarn>;2008</NoWarn>
|
||||
<DebugType>full</DebugType>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
|
||||
<OutputPath>..\bin\x64\Release\</OutputPath>
|
||||
<DefineConstants>TRACE;NETFX_CORE</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<NoWarn>;2008</NoWarn>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<OutputPath>..\bin\x86\Debug\</OutputPath>
|
||||
<DefineConstants>TRACE;DEBUG;NETFX_CORE;WINRT</DefineConstants>
|
||||
<NoWarn>;2008</NoWarn>
|
||||
<DebugType>full</DebugType>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
|
||||
<OutputPath>..\bin\x86\Release\</OutputPath>
|
||||
<DefineConstants>TRACE;NETFX_CORE;WINRT</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<NoWarn>;2008</NoWarn>
|
||||
<DebugType>none</DebugType>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="..\AnimationEx.WinRT.cs">
|
||||
<Link>AnimationEx.WinRT.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Int32Rect.cs">
|
||||
<Link>Int32Rect.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Location.cs">
|
||||
<Link>Location.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\LocationCollection.cs">
|
||||
<Link>LocationCollection.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Map.cs">
|
||||
<Link>Map.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Map.WinRT.cs">
|
||||
<Link>Map.WinRT.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\MapBase.cs">
|
||||
<Link>MapBase.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\MapBase.Silverlight.WinRT.cs">
|
||||
<Link>MapBase.Silverlight.WinRT.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\MapGraticule.cs">
|
||||
<Link>MapGraticule.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\MapGraticule.Silverlight.WinRT.cs">
|
||||
<Link>MapGraticule.Silverlight.WinRT.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\MapItem.cs">
|
||||
<Link>MapItem.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\MapItemsControl.cs">
|
||||
<Link>MapItemsControl.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\MapOverlay.cs">
|
||||
<Link>MapOverlay.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\MapOverlay.Silverlight.WinRT.cs">
|
||||
<Link>MapOverlay.Silverlight.WinRT.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\MapPanel.cs">
|
||||
<Link>MapPanel.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\MapPolygon.cs">
|
||||
<Link>MapPolygon.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\MapPolyline.cs">
|
||||
<Link>MapPolyline.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\MapPolyline.WinRT.cs">
|
||||
<Link>MapPolyline.WinRT.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\MapTransform.cs">
|
||||
<Link>MapTransform.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\MatrixEx.cs">
|
||||
<Link>MatrixEx.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\MercatorTransform.cs">
|
||||
<Link>MercatorTransform.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Pushpin.cs">
|
||||
<Link>Pushpin.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Tile.cs">
|
||||
<Link>Tile.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Tile.Silverlight.WinRT.cs">
|
||||
<Link>Tile.Silverlight.WinRT.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\TileContainer.cs">
|
||||
<Link>TileContainer.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\TileContainer.Silverlight.WinRT.cs">
|
||||
<Link>TileContainer.Silverlight.WinRT.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\TileImageLoader.Silverlight.WinRT.cs">
|
||||
<Link>TileImageLoader.Silverlight.WinRT.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\TileLayer.cs">
|
||||
<Link>TileLayer.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\TileLayer.Silverlight.WinRT.cs">
|
||||
<Link>TileLayer.Silverlight.WinRT.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\TileLayerCollection.cs">
|
||||
<Link>TileLayerCollection.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\TileSource.cs">
|
||||
<Link>TileSource.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\TransformEx.WinRT.cs">
|
||||
<Link>TransformEx.WinRT.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup />
|
||||
<ItemGroup>
|
||||
<Page Include="Themes\Generic.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Condition=" '$(VisualStudioVersion)' == '' or '$(VisualStudioVersion)' < '11.0' ">
|
||||
<VisualStudioVersion>11.0</VisualStudioVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<RunPostBuildEvent>OnBuildSuccess</RunPostBuildEvent>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(MSBuildExtensionsPath)\Microsoft\WindowsXaml\v$(VisualStudioVersion)\Microsoft.Windows.UI.Xaml.CSharp.targets" />
|
||||
<PropertyGroup>
|
||||
<PreBuildEvent>
|
||||
</PreBuildEvent>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<PostBuildEvent>
|
||||
</PostBuildEvent>
|
||||
</PropertyGroup>
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
||||
14
MapControl/WinRT/Properties/AssemblyInfo.cs
Normal file
14
MapControl/WinRT/Properties/AssemblyInfo.cs
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
[assembly: AssemblyTitle("MapControl.WinRT")]
|
||||
[assembly: AssemblyDescription("XAML Map Control for Windows Runtime")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("Clemens Fischer")]
|
||||
[assembly: AssemblyProduct("XAML Map Control")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2012 Clemens Fischer")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
[assembly: ComVisible(false)]
|
||||
4
MapControl/WinRT/ReadMe.txt
Normal file
4
MapControl/WinRT/ReadMe.txt
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
|
||||
The Visual Studio Project for Windows Runtime resides in a seperate folder
|
||||
MapControl/WinRT, because it needs to have its own Themes/Generic.xaml file.
|
||||
Output is generated to ../bin.
|
||||
64
MapControl/WinRT/Themes/Generic.xaml
Normal file
64
MapControl/WinRT/Themes/Generic.xaml
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:map="using:MapControl">
|
||||
<Style TargetType="map:MapItemsControl">
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate>
|
||||
<ItemsPresenter/>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Setter Property="ItemsPanel">
|
||||
<Setter.Value>
|
||||
<ItemsPanelTemplate>
|
||||
<map:MapPanel/>
|
||||
</ItemsPanelTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
<Style TargetType="map:MapItem">
|
||||
<Setter Property="HorizontalAlignment" Value="Stretch"/>
|
||||
<Setter Property="VerticalAlignment" Value="Stretch"/>
|
||||
<Setter Property="HorizontalContentAlignment" Value="Left"/>
|
||||
<Setter Property="VerticalContentAlignment" Value="Top"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="map:MapItem">
|
||||
<ContentPresenter Content="{TemplateBinding Content}"
|
||||
ContentTemplate="{TemplateBinding ContentTemplate}"
|
||||
Margin="{TemplateBinding Padding}"
|
||||
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
<Style TargetType="map:Pushpin">
|
||||
<Setter Property="HorizontalAlignment" Value="Left"/>
|
||||
<Setter Property="VerticalAlignment" Value="Bottom"/>
|
||||
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
|
||||
<Setter Property="VerticalContentAlignment" Value="Stretch"/>
|
||||
<Setter Property="Padding" Value="3"/>
|
||||
<Setter Property="Background" Value="White"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="map:Pushpin">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Rectangle Fill="{TemplateBinding Background}"/>
|
||||
<Path Grid.Row="1" Fill="{TemplateBinding Background}" Data="M 0,-0.5 L 0,16 16,-0.5"/>
|
||||
<ContentPresenter Content="{TemplateBinding Content}"
|
||||
ContentTemplate="{TemplateBinding ContentTemplate}"
|
||||
Margin="{TemplateBinding Padding}"
|
||||
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
|
||||
</Grid>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</ResourceDictionary>
|
||||
Loading…
Add table
Add a link
Reference in a new issue