diff --git a/Caching/FileDbCache/Properties/AssemblyInfo.cs b/Caching/FileDbCache/Properties/AssemblyInfo.cs index 402ea30f..b341c093 100644 --- a/Caching/FileDbCache/Properties/AssemblyInfo.cs +++ b/Caching/FileDbCache/Properties/AssemblyInfo.cs @@ -3,13 +3,12 @@ using System.Runtime.InteropServices; [assembly: AssemblyTitle("FileDbCache")] [assembly: AssemblyDescription("ObjectCache implementation based on EzTools FileDb")] - [assembly: AssemblyProduct("XAML Map Control")] [assembly: AssemblyCompany("Clemens Fischer")] [assembly: AssemblyCopyright("Copyright © 2014 Clemens Fischer")] [assembly: AssemblyTrademark("")] -[assembly: AssemblyVersion("1.12.1")] -[assembly: AssemblyFileVersion("1.12.1")] +[assembly: AssemblyVersion("2.0.0")] +[assembly: AssemblyFileVersion("2.0.0")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCulture("")] [assembly: ComVisible(false)] diff --git a/Caching/ImageFileCache/Properties/AssemblyInfo.cs b/Caching/ImageFileCache/Properties/AssemblyInfo.cs index ce0663f6..a43f045b 100644 --- a/Caching/ImageFileCache/Properties/AssemblyInfo.cs +++ b/Caching/ImageFileCache/Properties/AssemblyInfo.cs @@ -3,13 +3,12 @@ using System.Runtime.InteropServices; [assembly: AssemblyTitle("ImageFileCache")] [assembly: AssemblyDescription("ObjectCache implementation based on local image files")] - [assembly: AssemblyProduct("XAML Map Control")] [assembly: AssemblyCompany("Clemens Fischer")] [assembly: AssemblyCopyright("Copyright © 2014 Clemens Fischer")] [assembly: AssemblyTrademark("")] -[assembly: AssemblyVersion("1.12.1")] -[assembly: AssemblyFileVersion("1.12.1")] +[assembly: AssemblyVersion("2.0.0")] +[assembly: AssemblyFileVersion("2.0.0")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCulture("")] [assembly: ComVisible(false)] diff --git a/MapControl.sln b/MapControl.sln index af9fb9bf..28867e55 100644 --- a/MapControl.sln +++ b/MapControl.sln @@ -21,6 +21,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MapControl.WinRT", "MapCont EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WpfApplication", "SampleApps\WpfApplication\WpfApplication.csproj", "{9949326E-9261-4F95-89B1-151F60498951}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PhoneApplication", "SampleApps\PhoneApplication\PhoneApplication.csproj", "{8D0A57DF-FABF-4AEE-8768-9C18B2B43CA9}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -65,6 +67,12 @@ Global {9949326E-9261-4F95-89B1-151F60498951}.Debug|Any CPU.Build.0 = Debug|Any CPU {9949326E-9261-4F95-89B1-151F60498951}.Release|Any CPU.ActiveCfg = Release|Any CPU {9949326E-9261-4F95-89B1-151F60498951}.Release|Any CPU.Build.0 = Release|Any CPU + {8D0A57DF-FABF-4AEE-8768-9C18B2B43CA9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8D0A57DF-FABF-4AEE-8768-9C18B2B43CA9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8D0A57DF-FABF-4AEE-8768-9C18B2B43CA9}.Debug|Any CPU.Deploy.0 = Debug|Any CPU + {8D0A57DF-FABF-4AEE-8768-9C18B2B43CA9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8D0A57DF-FABF-4AEE-8768-9C18B2B43CA9}.Release|Any CPU.Build.0 = Release|Any CPU + {8D0A57DF-FABF-4AEE-8768-9C18B2B43CA9}.Release|Any CPU.Deploy.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/MapControl/MatrixEx.cs b/MapControl/Extensions.Silverlight.WinRT.cs similarity index 94% rename from MapControl/MatrixEx.cs rename to MapControl/Extensions.Silverlight.WinRT.cs index 6a3ac5a3..5ba06a01 100644 --- a/MapControl/MatrixEx.cs +++ b/MapControl/Extensions.Silverlight.WinRT.cs @@ -3,7 +3,7 @@ // Licensed under the Microsoft Public License (Ms-PL) using System; -#if NETFX_CORE +#if WINDOWS_RUNTIME using Windows.UI.Xaml.Media; #else using System.Windows.Media; @@ -11,12 +11,17 @@ using System.Windows.Media; namespace MapControl { - public static class MatrixEx + internal static partial class Extensions { + public static void Freeze(this object freezable) + { + } + public static Matrix Translate(this Matrix matrix, double offsetX, double offsetY) { matrix.OffsetX += offsetX; matrix.OffsetY += offsetY; + return matrix; } @@ -30,6 +35,7 @@ namespace MapControl 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)); } @@ -40,12 +46,14 @@ namespace MapControl 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, diff --git a/MapControl/AnimationEx.Silverlight.cs b/MapControl/Extensions.Silverlight.cs similarity index 93% rename from MapControl/AnimationEx.Silverlight.cs rename to MapControl/Extensions.Silverlight.cs index ed662105..d3475f7b 100644 --- a/MapControl/AnimationEx.Silverlight.cs +++ b/MapControl/Extensions.Silverlight.cs @@ -7,7 +7,7 @@ using System.Windows.Media.Animation; namespace MapControl { - public static class AnimationEx + internal static partial class Extensions { public static void BeginAnimation(this DependencyObject obj, DependencyProperty property, Timeline animation) { diff --git a/MapControl/AnimationEx.WinRT.cs b/MapControl/Extensions.WinRT.cs similarity index 86% rename from MapControl/AnimationEx.WinRT.cs rename to MapControl/Extensions.WinRT.cs index 25939843..db7cefbf 100644 --- a/MapControl/AnimationEx.WinRT.cs +++ b/MapControl/Extensions.WinRT.cs @@ -2,13 +2,20 @@ // Copyright © 2014 Clemens Fischer // Licensed under the Microsoft Public License (Ms-PL) +using Windows.Foundation; using Windows.UI.Xaml; +using Windows.UI.Xaml.Media; using Windows.UI.Xaml.Media.Animation; namespace MapControl { - public static class AnimationEx + internal static partial class Extensions { + public static Point Transform(this GeneralTransform transform, Point point) + { + return transform.TransformPoint(point); + } + public static void BeginAnimation(this DependencyObject obj, DependencyProperty property, DoubleAnimation animation) { animation.EnableDependentAnimation = true; diff --git a/MapControl/GlyphRunText.cs b/MapControl/GlyphRunText.cs index 04b9de2b..ec04a8a9 100644 --- a/MapControl/GlyphRunText.cs +++ b/MapControl/GlyphRunText.cs @@ -13,7 +13,7 @@ namespace MapControl /// public static class GlyphRunText { - public static GlyphRun Create(string text, Typeface typeface, double emSize, Point baselineOrigin) + public static GlyphRun Create(string text, Typeface typeface, double emSize, Point baselineOrigin = new Point()) { GlyphTypeface glyphTypeface; @@ -32,36 +32,42 @@ namespace MapControl advanceWidths[i] = glyphTypeface.AdvanceWidths[glyphIndex] * emSize; } - return new GlyphRun(glyphTypeface, 0, false, emSize, glyphIndices, baselineOrigin, advanceWidths, - null, null, null, null, null, null); + return new GlyphRun(glyphTypeface, 0, false, emSize, glyphIndices, baselineOrigin, advanceWidths, null, null, null, null, null, null); } - public static GlyphRun Create(string text, Typeface typeface, double emSize, Vector centerOffset) + public static void DrawGlyphRun(this DrawingContext drawingContext, Brush foreground, GlyphRun glyphRun, + Point position, HorizontalAlignment horizontalAlignment, VerticalAlignment verticalAlignment) { - GlyphTypeface glyphTypeface; - - if (!typeface.TryGetGlyphTypeface(out glyphTypeface)) - { - throw new ArgumentException(string.Format("{0}: no GlyphTypeface found", typeface.FontFamily)); - } - - var glyphIndices = new ushort[text.Length]; - var advanceWidths = new double[text.Length]; - - for (int i = 0; i < text.Length; i++) - { - var glyphIndex = glyphTypeface.CharacterToGlyphMap[text[i]]; - glyphIndices[i] = glyphIndex; - advanceWidths[i] = glyphTypeface.AdvanceWidths[glyphIndex] * emSize; - } - - 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); + var transform = new TranslateTransform(position.X - bbox.X, position.Y - bbox.Y); - return new GlyphRun(glyphTypeface, 0, false, emSize, glyphIndices, baselineOrigin, advanceWidths, - null, null, null, null, null, null); + switch (horizontalAlignment) + { + case HorizontalAlignment.Center: + transform.X -= bbox.Width / 2d; + break; + case HorizontalAlignment.Right: + transform.X -= bbox.Width; + break; + default: + break; + } + + switch (verticalAlignment) + { + case VerticalAlignment.Center: + transform.Y -= bbox.Height / 2d; + break; + case VerticalAlignment.Bottom: + transform.Y -= bbox.Height; + break; + default: + break; + } + + drawingContext.PushTransform(transform); + drawingContext.DrawGlyphRun(foreground, glyphRun); + drawingContext.Pop(); } } } diff --git a/MapControl/MapImage.WPF.cs b/MapControl/IMapElement.cs similarity index 62% rename from MapControl/MapImage.WPF.cs rename to MapControl/IMapElement.cs index 28f23574..33faaa3b 100644 --- a/MapControl/MapImage.WPF.cs +++ b/MapControl/IMapElement.cs @@ -4,11 +4,8 @@ namespace MapControl { - public partial class MapImage + public interface IMapElement { - static MapImage() - { - imageTransform.Freeze(); - } + MapBase ParentMap { get; set; } } } diff --git a/MapControl/ImageTileSource.cs b/MapControl/ImageTileSource.Silverlight.WinRT.cs similarity index 68% rename from MapControl/ImageTileSource.cs rename to MapControl/ImageTileSource.Silverlight.WinRT.cs index e4ff935a..8c7cfcc9 100644 --- a/MapControl/ImageTileSource.cs +++ b/MapControl/ImageTileSource.Silverlight.WinRT.cs @@ -2,7 +2,7 @@ // Copyright © 2014 Clemens Fischer // Licensed under the Microsoft Public License (Ms-PL) -#if NETFX_CORE +#if WINDOWS_RUNTIME using Windows.UI.Xaml.Media; using Windows.UI.Xaml.Media.Imaging; #else @@ -13,13 +13,11 @@ using System.Windows.Media.Imaging; namespace MapControl { /// - /// Provides the image of a map tile. ImageTileSource bypasses downloading and - /// optional caching in TileImageLoader. By overriding the LoadImage method, + /// Provides the image of a map tile. ImageTileSource bypasses image + /// downloading in TileImageLoader. By overriding the LoadImage method, /// an application can provide tile images from an arbitrary source. - /// WPF only: If the CanLoadAsync property is true, LoadImage will be called - /// from a separate, non-UI thread and must hence return a frozen ImageSource. /// - public partial class ImageTileSource : TileSource + public class ImageTileSource : TileSource { public virtual ImageSource LoadImage(int x, int y, int zoomLevel) { diff --git a/MapControl/ImageTileSource.WPF.cs b/MapControl/ImageTileSource.WPF.cs index f9aafcd9..2af21f5b 100644 --- a/MapControl/ImageTileSource.WPF.cs +++ b/MapControl/ImageTileSource.WPF.cs @@ -2,13 +2,52 @@ // Copyright © 2014 Clemens Fischer // Licensed under the Microsoft Public License (Ms-PL) +using System.IO; +using System.Net; +using System.Windows.Media; +using System.Windows.Media.Imaging; + namespace MapControl { - public partial class ImageTileSource + /// + /// Provides the image of a map tile. ImageTileSource bypasses image downloading + /// and optional caching in TileImageLoader. By overriding the LoadImage method, + /// an application can provide tile images from an arbitrary source. + /// If the IsAsync property is true, LoadImage will be called from a separate, + /// non-UI thread and must therefore return a frozen ImageSource. + /// + public class ImageTileSource : TileSource { - public virtual bool CanLoadAsync + public bool IsAsync { get; set; } + + public virtual ImageSource LoadImage(int x, int y, int zoomLevel) { - get { return false; } + ImageSource image = null; + + var uri = GetUri(x, y, zoomLevel); + + if (uri != null) + { + if (IsAsync) + { + var buffer = new WebClient().DownloadData(uri); + + if (buffer != null) + { + using (var stream = new MemoryStream(buffer)) + { + image = BitmapFrame.Create(stream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad); + image.Freeze(); + } + } + } + else + { + image = BitmapFrame.Create(uri); + } + } + + return image; } } } diff --git a/MapControl/Location.cs b/MapControl/Location.cs index 89286a46..47f38fb9 100644 --- a/MapControl/Location.cs +++ b/MapControl/Location.cs @@ -10,9 +10,6 @@ namespace MapControl /// /// A geographic location with latitude and longitude values in degrees. /// -#if !SILVERLIGHT && !NETFX_CORE - [Serializable] -#endif public partial class Location { private double latitude; diff --git a/MapControl/LocationConverter.cs b/MapControl/LocationConverter.cs index 1c9c8bde..e8f48463 100644 --- a/MapControl/LocationConverter.cs +++ b/MapControl/LocationConverter.cs @@ -22,6 +22,9 @@ namespace MapControl } [TypeConverter(typeof(LocationConverter))] +#if !SILVERLIGHT + [Serializable] +#endif public partial class Location { } diff --git a/MapControl/Map.WinRT.cs b/MapControl/Map.WinRT.cs index e0f93aa5..bf163201 100644 --- a/MapControl/Map.WinRT.cs +++ b/MapControl/Map.WinRT.cs @@ -21,7 +21,7 @@ namespace MapControl public Map() { - ManipulationMode = ManipulationModes.Scale | ManipulationModes.ScaleInertia | + ManipulationMode = ManipulationModes.Scale | ManipulationModes.TranslateX | ManipulationModes.TranslateY | ManipulationModes.TranslateInertia; ManipulationDelta += OnManipulationDelta; diff --git a/MapControl/MapBase.Silverlight.WinRT.cs b/MapControl/MapBase.Silverlight.WinRT.cs index 0877bb12..d1b207a0 100644 --- a/MapControl/MapBase.Silverlight.WinRT.cs +++ b/MapControl/MapBase.Silverlight.WinRT.cs @@ -2,7 +2,7 @@ // Copyright © 2014 Clemens Fischer // Licensed under the Microsoft Public License (Ms-PL) -#if NETFX_CORE +#if WINDOWS_RUNTIME using Windows.Foundation; using Windows.UI; using Windows.UI.Xaml; @@ -47,7 +47,6 @@ namespace MapControl { Background = new SolidColorBrush(Colors.Transparent); Clip = new RectangleGeometry(); - Children.Add(tileContainer); SizeChanged += OnRenderSizeChanged; } diff --git a/MapControl/MapBase.WPF.cs b/MapControl/MapBase.WPF.cs index 20bbc897..8f03fc95 100644 --- a/MapControl/MapBase.WPF.cs +++ b/MapControl/MapBase.WPF.cs @@ -52,31 +52,11 @@ namespace MapControl typeof(MapBase), new FrameworkPropertyMetadata(Brushes.Transparent)); } - partial void Initialize() - { - AddVisualChild(tileContainer); - } - partial void RemoveAnimation(DependencyProperty property) { BeginAnimation(property, null); } - protected override int VisualChildrenCount - { - get { return base.VisualChildrenCount + 1; } - } - - protected override Visual GetVisualChild(int index) - { - if (index == 0) - { - return tileContainer; - } - - return base.GetVisualChild(index - 1); - } - protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo) { base.OnRenderSizeChanged(sizeInfo); diff --git a/MapControl/MapBase.cs b/MapControl/MapBase.cs index dd65cbe7..de840b01 100644 --- a/MapControl/MapBase.cs +++ b/MapControl/MapBase.cs @@ -5,7 +5,7 @@ using System; using System.Collections.Specialized; using System.Linq; -#if NETFX_CORE +#if WINDOWS_RUNTIME using Windows.Foundation; using Windows.UI.Xaml; using Windows.UI.Xaml.Media; @@ -74,6 +74,7 @@ namespace MapControl SetParentMap(); TileLayers = new TileLayerCollection(); + InternalChildren.Add(tileContainer); Initialize(); Loaded += OnLoaded; diff --git a/MapControl/MapControl.Silverlight.csproj b/MapControl/MapControl.Silverlight.csproj index 23e1528e..864aa374 100644 --- a/MapControl/MapControl.Silverlight.csproj +++ b/MapControl/MapControl.Silverlight.csproj @@ -67,8 +67,12 @@ - - + + + + Code + + @@ -84,6 +88,8 @@ + + @@ -92,8 +98,8 @@ - + @@ -102,7 +108,6 @@ - diff --git a/MapControl/MapControl.WPF.csproj b/MapControl/MapControl.WPF.csproj index 0a1165ba..d24d60a8 100644 --- a/MapControl/MapControl.WPF.csproj +++ b/MapControl/MapControl.WPF.csproj @@ -52,8 +52,8 @@ - + @@ -64,20 +64,20 @@ - + + - @@ -89,7 +89,6 @@ - diff --git a/MapControl/MapGraticule.Silverlight.WinRT.cs b/MapControl/MapGraticule.Silverlight.WinRT.cs index 546c5933..8ae43dd2 100644 --- a/MapControl/MapGraticule.Silverlight.WinRT.cs +++ b/MapControl/MapGraticule.Silverlight.WinRT.cs @@ -5,9 +5,8 @@ using System; using System.Collections.Generic; using System.Linq; -#if NETFX_CORE +#if WINDOWS_RUNTIME using Windows.Foundation; -using Windows.UI.Text; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Media; @@ -23,40 +22,8 @@ using System.Windows.Data; namespace MapControl { - public partial class MapGraticule : MapPanel + public partial class MapGraticule { - public static readonly DependencyProperty FontFamilyProperty = DependencyProperty.Register( - "FontFamily", typeof(FontFamily), typeof(MapGraticule), - new PropertyMetadata(default(FontFamily), (o, e) => ((MapGraticule)o).OnViewportChanged())); - - public static readonly DependencyProperty FontSizeProperty = DependencyProperty.Register( - "FontSize", typeof(double), typeof(MapGraticule), - new PropertyMetadata(10d, (o, e) => ((MapGraticule)o).OnViewportChanged())); - - public static readonly DependencyProperty FontStyleProperty = DependencyProperty.Register( - "FontStyle", typeof(FontStyle), typeof(MapGraticule), - new PropertyMetadata(default(FontStyle), (o, e) => ((MapGraticule)o).OnViewportChanged())); - - public static readonly DependencyProperty FontStretchProperty = DependencyProperty.Register( - "FontStretch", typeof(FontStretch), typeof(MapGraticule), - new PropertyMetadata(default(FontStretch), (o, e) => ((MapGraticule)o).OnViewportChanged())); - - public static readonly DependencyProperty FontWeightProperty = DependencyProperty.Register( - "FontWeight", typeof(FontWeight), typeof(MapGraticule), - new PropertyMetadata(FontWeights.Normal, (o, e) => ((MapGraticule)o).OnViewportChanged())); - - public static readonly DependencyProperty ForegroundProperty = DependencyProperty.Register( - "Foreground", typeof(Brush), typeof(MapGraticule), - new PropertyMetadata(null, (o, e) => ((MapGraticule)o).OnViewportChanged())); - - public static readonly DependencyProperty StrokeProperty = DependencyProperty.Register( - "Stroke", typeof(Brush), typeof(MapGraticule), - new PropertyMetadata(null, (o, e) => ((MapGraticule)o).path.Stroke = (Brush)e.NewValue)); - - public static readonly DependencyProperty StrokeThicknessProperty = DependencyProperty.Register( - "StrokeThickness", typeof(double), typeof(MapGraticule), - new PropertyMetadata(0.5, (o, e) => ((MapGraticule)o).path.StrokeThickness = (double)e.NewValue)); - private readonly Path path; private Location graticuleStart; private Location graticuleEnd; @@ -64,247 +31,181 @@ namespace MapControl public MapGraticule() { IsHitTestVisible = false; + StrokeThickness = 0.5; path = new Path { - Stroke = Stroke, - StrokeThickness = StrokeThickness + Data = new PathGeometry() }; + path.SetBinding(Shape.StrokeProperty, new Binding + { + Source = this, + Path = new PropertyPath("Stroke") + }); + + path.SetBinding(Shape.StrokeThicknessProperty, new Binding + { + Source = this, + Path = new PropertyPath("StrokeThickness") + }); + Children.Add(path); } - public FontFamily FontFamily - { - get { return (FontFamily)GetValue(FontFamilyProperty); } - set { SetValue(FontFamilyProperty, value); } - } - - public double FontSize - { - get { return (double)GetValue(FontSizeProperty); } - set { SetValue(FontSizeProperty, value); } - } - - public FontStyle FontStyle - { - 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 - { - get { return (Brush)GetValue(ForegroundProperty); } - set { SetValue(ForegroundProperty, value); } - } - - public Brush Stroke - { - get { return (Brush)GetValue(StrokeProperty); } - set { SetValue(StrokeProperty, value); } - } - - public double StrokeThickness - { - get { return (double)GetValue(StrokeThicknessProperty); } - set { SetValue(StrokeThicknessProperty, value); } - } - protected override void OnViewportChanged() { - if (ParentMap != null) + var geometry = (PathGeometry)path.Data; + var bounds = ParentMap.ViewportTransform.Inverse.TransformBounds(new Rect(new Point(), 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) { - if (path.Data == null) + spacing = LineSpacings.FirstOrDefault(s => s >= minSpacing); + } + + var labelStart = new Location( + Math.Ceiling(start.Latitude / spacing) * spacing, + Math.Ceiling(start.Longitude / spacing) * spacing); + + var labelEnd = new Location( + Math.Floor(end.Latitude / spacing) * spacing, + Math.Floor(end.Longitude / spacing) * spacing); + + var lineStart = new Location( + Math.Min(Math.Max(labelStart.Latitude - spacing, -ParentMap.MapTransform.MaxLatitude), ParentMap.MapTransform.MaxLatitude), + labelStart.Longitude - spacing); + + var lineEnd = new Location( + Math.Min(Math.Max(labelEnd.Latitude + spacing, -ParentMap.MapTransform.MaxLatitude), ParentMap.MapTransform.MaxLatitude), + labelEnd.Longitude + spacing); + + if (!lineStart.Equals(graticuleStart) || !lineEnd.Equals(graticuleEnd)) + { + graticuleStart = lineStart; + graticuleEnd = lineEnd; + + geometry.Figures.Clear(); + geometry.Transform = ParentMap.ViewportTransform; + + var latLocations = new List((int)((end.Latitude - labelStart.Latitude) / spacing) + 1); + + for (var lat = labelStart.Latitude; lat <= end.Latitude; lat += spacing) { - path.Data = new PathGeometry(); + var location = new Location(lat, lineStart.Longitude); + latLocations.Add(location); - if (Foreground == null) + var figure = new PathFigure { - SetBinding(ForegroundProperty, new Binding() - { - Source = ParentMap, - Path = new PropertyPath("Foreground") - }); - } + StartPoint = ParentMap.MapTransform.Transform(location), + IsClosed = false, + IsFilled = false + }; - if (Stroke == null) + location.Longitude = lineEnd.Longitude; + + figure.Segments.Add(new LineSegment { - SetBinding(StrokeProperty, new Binding() - { - Source = ParentMap, - Path = new PropertyPath("Foreground") - }); - } + Point = ParentMap.MapTransform.Transform(location), + }); + + geometry.Figures.Add(figure); } - var geometry = (PathGeometry)path.Data; - var bounds = ParentMap.ViewportTransform.Inverse.TransformBounds(new Rect(new Point(), 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) + for (var lon = labelStart.Longitude; lon <= end.Longitude; lon += spacing) { - spacing = LineSpacings.FirstOrDefault(s => s >= minSpacing); + var figure = new PathFigure + { + StartPoint = ParentMap.MapTransform.Transform(new Location(lineStart.Latitude, lon)), + IsClosed = false, + IsFilled = false + }; + + figure.Segments.Add(new LineSegment + { + Point = ParentMap.MapTransform.Transform(new Location(lineEnd.Latitude, lon)), + }); + + geometry.Figures.Add(figure); } - var labelStart = new Location( - Math.Ceiling(start.Latitude / spacing) * spacing, - Math.Ceiling(start.Longitude / spacing) * spacing); + var childIndex = 1; // 0 for Path + var format = spacing < 1d ? "{0} {1}°{2:00}'" : "{0} {1}°"; + var measureSize = new Size(double.PositiveInfinity, double.PositiveInfinity); - var labelEnd = new Location( - Math.Floor(end.Latitude / spacing) * spacing, - Math.Floor(end.Longitude / spacing) * spacing); - - var lineStart = new Location( - Math.Min(Math.Max(labelStart.Latitude - spacing, -ParentMap.MapTransform.MaxLatitude), ParentMap.MapTransform.MaxLatitude), - labelStart.Longitude - spacing); - - var lineEnd = new Location( - Math.Min(Math.Max(labelEnd.Latitude + spacing, -ParentMap.MapTransform.MaxLatitude), ParentMap.MapTransform.MaxLatitude), - labelEnd.Longitude + spacing); - - if (!lineStart.Equals(graticuleStart) || !lineEnd.Equals(graticuleEnd)) + foreach (var location in latLocations) { - graticuleStart = lineStart; - graticuleEnd = lineEnd; - - geometry.Figures.Clear(); - geometry.Transform = ParentMap.ViewportTransform; - - var latLocations = new List((int)((end.Latitude - labelStart.Latitude) / spacing) + 1); - - for (var lat = labelStart.Latitude; lat <= end.Latitude; lat += spacing) - { - var location = new Location(lat, lineStart.Longitude); - latLocations.Add(location); - - var figure = new PathFigure - { - StartPoint = ParentMap.MapTransform.Transform(location), - IsClosed = false, - IsFilled = false - }; - - location.Longitude = lineEnd.Longitude; - - figure.Segments.Add(new LineSegment - { - Point = ParentMap.MapTransform.Transform(location), - }); - - geometry.Figures.Add(figure); - } - for (var lon = labelStart.Longitude; lon <= end.Longitude; lon += spacing) { - var figure = new PathFigure + location.Longitude = lon; + + TextBlock label; + + if (childIndex < Children.Count) { - StartPoint = ParentMap.MapTransform.Transform(new Location(lineStart.Latitude, lon)), - IsClosed = false, - IsFilled = false - }; - - figure.Segments.Add(new LineSegment - { - Point = ParentMap.MapTransform.Transform(new Location(lineEnd.Latitude, lon)), - }); - - geometry.Figures.Add(figure); - } - - var childIndex = 1; // 0 for Path - var format = spacing < 1d ? "{0} {1}°{2:00}'" : "{0} {1}°"; - var measureSize = new Size(double.PositiveInfinity, double.PositiveInfinity); - - foreach (var location in latLocations) - { - for (var lon = labelStart.Longitude; lon <= end.Longitude; lon += spacing) - { - location.Longitude = lon; - - TextBlock label; - - if (childIndex < Children.Count) - { - label = (TextBlock)Children[childIndex]; - } - else - { - label = new TextBlock - { - RenderTransform = new TransformGroup() - }; - - label.SetBinding(TextBlock.ForegroundProperty, new Binding - { - Source = this, - Path = new PropertyPath("Foreground") - }); - - Children.Add(label); - } - - childIndex++; - - if (FontFamily != null) - { - label.FontFamily = FontFamily; - } - - label.FontSize = FontSize; - label.FontStyle = FontStyle; - label.FontStretch = FontStretch; - label.FontWeight = FontWeight; - - label.Text = string.Format("{0}\n{1}", - CoordinateString(location.Latitude, format, "NS"), - CoordinateString(Location.NormalizeLongitude(location.Longitude), format, "EW")); - - label.Measure(measureSize); - - var transformGroup = (TransformGroup)label.RenderTransform; - - if (transformGroup.Children.Count == 0) - { - transformGroup.Children.Add(new TranslateTransform()); - transformGroup.Children.Add(ParentMap.RotateTransform); - transformGroup.Children.Add(new TranslateTransform()); - } - - var translateTransform = (TranslateTransform)transformGroup.Children[0]; - translateTransform.X = StrokeThickness / 2d + 2d; - translateTransform.Y = -label.DesiredSize.Height / 2d; - - var viewportPosition = ParentMap.LocationToViewportPoint(location); - translateTransform = (TranslateTransform)transformGroup.Children[2]; - translateTransform.X = viewportPosition.X; - translateTransform.Y = viewportPosition.Y; + label = (TextBlock)Children[childIndex]; } - } + else + { + label = new TextBlock + { + RenderTransform = new TransformGroup() + }; - while (Children.Count > childIndex) - { - Children.RemoveAt(Children.Count - 1); + label.SetBinding(TextBlock.ForegroundProperty, new Binding + { + Source = this, + Path = new PropertyPath("Foreground") + }); + + Children.Add(label); + } + + childIndex++; + + if (FontFamily != null) + { + label.FontFamily = FontFamily; + } + + label.FontSize = FontSize; + label.FontStyle = FontStyle; + label.FontStretch = FontStretch; + label.FontWeight = FontWeight; + + label.Text = string.Format("{0}\n{1}", + CoordinateString(location.Latitude, format, "NS"), + CoordinateString(Location.NormalizeLongitude(location.Longitude), format, "EW")); + + label.Measure(measureSize); + + var transformGroup = (TransformGroup)label.RenderTransform; + + if (transformGroup.Children.Count == 0) + { + transformGroup.Children.Add(new TranslateTransform()); + transformGroup.Children.Add(ParentMap.RotateTransform); + transformGroup.Children.Add(new TranslateTransform()); + } + + var translateTransform = (TranslateTransform)transformGroup.Children[0]; + translateTransform.X = StrokeThickness / 2d + 2d; + translateTransform.Y = -label.DesiredSize.Height / 2d; + + var viewportPosition = ParentMap.LocationToViewportPoint(location); + translateTransform = (TranslateTransform)transformGroup.Children[2]; + translateTransform.X = viewportPosition.X; + translateTransform.Y = viewportPosition.Y; } } - } - else - { - path.Data = null; + + while (Children.Count > childIndex) + { + Children.RemoveAt(Children.Count - 1); + } } base.OnViewportChanged(); diff --git a/MapControl/MapGraticule.WPF.cs b/MapControl/MapGraticule.WPF.cs index 9da38a78..8111b850 100644 --- a/MapControl/MapGraticule.WPF.cs +++ b/MapControl/MapGraticule.WPF.cs @@ -10,7 +10,7 @@ using System.Windows.Media; namespace MapControl { - public partial class MapGraticule : MapOverlay + public partial class MapGraticule { private class Label { diff --git a/MapControl/MapGraticule.cs b/MapControl/MapGraticule.cs index 3a3808f6..c5003d9a 100644 --- a/MapControl/MapGraticule.cs +++ b/MapControl/MapGraticule.cs @@ -2,7 +2,7 @@ // Copyright © 2014 Clemens Fischer // Licensed under the Microsoft Public License (Ms-PL) -#if NETFX_CORE +#if WINDOWS_RUNTIME using Windows.UI.Xaml; #else using System.Windows; @@ -13,7 +13,7 @@ namespace MapControl /// /// Draws a graticule overlay. /// - public partial class MapGraticule + public partial class MapGraticule : MapOverlay { /// /// Graticule line spacings in degrees. diff --git a/MapControl/MapImage.cs b/MapControl/MapImage.cs index 74e9fd6c..b890f429 100644 --- a/MapControl/MapImage.cs +++ b/MapControl/MapImage.cs @@ -2,7 +2,7 @@ // Copyright © 2014 Clemens Fischer // Licensed under the Microsoft Public License (Ms-PL) -#if NETFX_CORE +#if WINDOWS_RUNTIME using Windows.UI.Xaml; using Windows.UI.Xaml.Media; #else @@ -15,13 +15,8 @@ namespace MapControl /// /// Fills a rectangular area with an ImageBrush from the Source property. /// - public partial class MapImage : MapRectangle + public class MapImage : MapRectangle { - private static readonly MatrixTransform imageTransform = new MatrixTransform - { - Matrix = new Matrix(1d, 0d, 0d, -1d, 0d, 1d) - }; - public static readonly DependencyProperty SourceProperty = DependencyProperty.Register( "Source", typeof(ImageSource), typeof(MapImage), new PropertyMetadata(null, (o, e) => ((MapImage)o).SourceChanged((ImageSource)e.NewValue))); @@ -34,13 +29,17 @@ namespace MapControl private void SourceChanged(ImageSource image) { - var imageBrush = new ImageBrush + var transform = new MatrixTransform + { + Matrix = new Matrix(1d, 0d, 0d, -1d, 0d, 1d) + }; + transform.Freeze(); + + Fill = new ImageBrush { ImageSource = image, - RelativeTransform = imageTransform + RelativeTransform = transform }; - - Fill = imageBrush; } } } diff --git a/MapControl/MapImageLayer.Silverlight.WinRT.cs b/MapControl/MapImageLayer.Silverlight.WinRT.cs index d697ba6a..3dde5f53 100644 --- a/MapControl/MapImageLayer.Silverlight.WinRT.cs +++ b/MapControl/MapImageLayer.Silverlight.WinRT.cs @@ -2,7 +2,7 @@ // Copyright © 2014 Clemens Fischer // Licensed under the Microsoft Public License (Ms-PL) -#if NETFX_CORE +#if WINDOWS_RUNTIME using Windows.UI.Xaml; using Windows.UI.Xaml.Media.Imaging; #else diff --git a/MapControl/MapImageLayer.cs b/MapControl/MapImageLayer.cs index 9aa5bc97..e17fd437 100644 --- a/MapControl/MapImageLayer.cs +++ b/MapControl/MapImageLayer.cs @@ -4,7 +4,7 @@ using System; using System.Globalization; -#if NETFX_CORE +#if WINDOWS_RUNTIME using Windows.Foundation; using Windows.UI.Xaml; using Windows.UI.Xaml.Media.Imaging; @@ -177,8 +177,8 @@ namespace MapControl private void BlendImages() { -#if NETFX_CORE - var duration = TimeSpan.Zero; // animation not working in WinRT (?) +#if WINDOWS_RUNTIME + var duration = TimeSpan.Zero; // animation not working in Windows Runtime (?) #else var duration = Tile.AnimationDuration; #endif diff --git a/MapControl/MapItem.Silverlight.WinRT.cs b/MapControl/MapItem.Silverlight.WinRT.cs index c4edc04f..13faa675 100644 --- a/MapControl/MapItem.Silverlight.WinRT.cs +++ b/MapControl/MapItem.Silverlight.WinRT.cs @@ -2,7 +2,7 @@ // Copyright © 2014 Clemens Fischer // Licensed under the Microsoft Public License (Ms-PL) -#if NETFX_CORE +#if WINDOWS_RUNTIME using Windows.UI.Xaml.Controls; #else using System.Windows.Controls; diff --git a/MapControl/MapItemsControl.Silverlight.WinRT.cs b/MapControl/MapItemsControl.Silverlight.WinRT.cs index 1787444c..f9cade87 100644 --- a/MapControl/MapItemsControl.Silverlight.WinRT.cs +++ b/MapControl/MapItemsControl.Silverlight.WinRT.cs @@ -2,7 +2,7 @@ // Copyright © 2014 Clemens Fischer // Licensed under the Microsoft Public License (Ms-PL) -#if NETFX_CORE +#if WINDOWS_RUNTIME using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; #else diff --git a/MapControl/MapOverlay.Silverlight.WinRT.cs b/MapControl/MapOverlay.Silverlight.WinRT.cs new file mode 100644 index 00000000..aeed4db4 --- /dev/null +++ b/MapControl/MapOverlay.Silverlight.WinRT.cs @@ -0,0 +1,112 @@ +// XAML Map Control - http://xamlmapcontrol.codeplex.com/ +// Copyright © 2014 Clemens Fischer +// Licensed under the Microsoft Public License (Ms-PL) + +#if WINDOWS_RUNTIME +using Windows.UI.Text; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Data; +using Windows.UI.Xaml.Media; +#else +using System.Windows; +using System.Windows.Data; +using System.Windows.Media; +#endif + +namespace MapControl +{ + public partial class MapOverlay + { + public static readonly DependencyProperty FontSizeProperty = DependencyProperty.Register( + "FontSize", typeof(double), typeof(MapOverlay), new PropertyMetadata(10d)); + + public static readonly DependencyProperty FontFamilyProperty = DependencyProperty.Register( + "FontFamily", typeof(FontFamily), typeof(MapOverlay), new PropertyMetadata(default(FontFamily))); + + public static readonly DependencyProperty FontStyleProperty = DependencyProperty.Register( + "FontStyle", typeof(FontStyle), typeof(MapOverlay), new PropertyMetadata(default(FontStyle))); + + public static readonly DependencyProperty FontStretchProperty = DependencyProperty.Register( + "FontStretch", typeof(FontStretch), typeof(MapOverlay), new PropertyMetadata(default(FontStretch))); + + public static readonly DependencyProperty FontWeightProperty = DependencyProperty.Register( + "FontWeight", typeof(FontWeight), typeof(MapOverlay), new PropertyMetadata(default(FontWeight))); + + public static readonly DependencyProperty ForegroundProperty = DependencyProperty.Register( + "Foreground", typeof(Brush), typeof(MapOverlay), new PropertyMetadata(null)); + + public static readonly DependencyProperty StrokeProperty = DependencyProperty.Register( + "Stroke", typeof(Brush), typeof(MapOverlay), new PropertyMetadata(null)); + + public static readonly DependencyProperty StrokeThicknessProperty = DependencyProperty.Register( + "StrokeThickness", typeof(double), typeof(MapOverlay), new PropertyMetadata(1d)); + + public static readonly DependencyProperty StrokeDashArrayProperty = DependencyProperty.Register( + "StrokeDashArray", typeof(DoubleCollection), typeof(MapOverlay), new PropertyMetadata(null)); + + public static readonly DependencyProperty StrokeDashOffsetProperty = DependencyProperty.Register( + "StrokeDashOffset", typeof(double), typeof(MapOverlay), new PropertyMetadata(0d)); + + public static readonly DependencyProperty StrokeDashCapProperty = DependencyProperty.Register( + "StrokeDashCap", typeof(PenLineCap), typeof(MapOverlay), new PropertyMetadata(default(PenLineCap))); + + public static readonly DependencyProperty StrokeStartLineCapProperty = DependencyProperty.Register( + "StrokeStartLineCap", typeof(PenLineCap), typeof(MapOverlay), new PropertyMetadata(default(PenLineCap))); + + public static readonly DependencyProperty StrokeEndLineCapProperty = DependencyProperty.Register( + "StrokeEndLineCap", typeof(PenLineCap), typeof(MapOverlay), new PropertyMetadata(default(PenLineCap))); + + public static readonly DependencyProperty StrokeLineJoinProperty = DependencyProperty.Register( + "StrokeLineJoin", typeof(PenLineJoin), typeof(MapOverlay), new PropertyMetadata(default(PenLineJoin))); + + public static readonly DependencyProperty StrokeMiterLimitProperty = DependencyProperty.Register( + "StrokeMiterLimit", typeof(double), typeof(MapOverlay), new PropertyMetadata(1d)); + + private Binding foregroundBinding; + private Binding strokeBinding; + + public override MapBase ParentMap + { + get { return base.ParentMap; } + set + { + if (foregroundBinding != null) + { + foregroundBinding = null; + ClearValue(ForegroundProperty); + } + + if (strokeBinding != null) + { + strokeBinding = null; + ClearValue(StrokeProperty); + } + + if (value != null) + { + if (Foreground == null) + { + foregroundBinding = new Binding + { + Source = value, + Path = new PropertyPath("Foreground") + }; + SetBinding(ForegroundProperty, foregroundBinding); + } + + if (Stroke == null) + { + strokeBinding = new Binding + { + Source = value, + Path = new PropertyPath("Foreground") + }; + SetBinding(StrokeProperty, strokeBinding); + } + } + + base.ParentMap = value; + } + } + } +} diff --git a/MapControl/MapOverlay.WPF.cs b/MapControl/MapOverlay.WPF.cs new file mode 100644 index 00000000..bce2be5f --- /dev/null +++ b/MapControl/MapOverlay.WPF.cs @@ -0,0 +1,100 @@ +// XAML Map Control - http://xamlmapcontrol.codeplex.com/ +// Copyright © 2014 Clemens Fischer +// Licensed under the Microsoft Public License (Ms-PL) + +using System.Windows; +using System.Windows.Controls; +using System.Windows.Media; +using System.Windows.Shapes; + +namespace MapControl +{ + public partial class 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) => { if (o.GetValue(StrokeProperty) == null) ((MapOverlay)o).pen = null; })); + + public static readonly DependencyProperty StrokeProperty = Shape.StrokeProperty.AddOwner( + typeof(MapOverlay), new FrameworkPropertyMetadata((o, e) => ((MapOverlay)o).pen = null)); + + public static readonly DependencyProperty StrokeThicknessProperty = Shape.StrokeThicknessProperty.AddOwner( + typeof(MapOverlay), new FrameworkPropertyMetadata((o, e) => ((MapOverlay)o).pen = null)); + + public static readonly DependencyProperty StrokeDashArrayProperty = Shape.StrokeDashArrayProperty.AddOwner( + typeof(MapOverlay), new FrameworkPropertyMetadata((o, e) => ((MapOverlay)o).pen = null)); + + public static readonly DependencyProperty StrokeDashOffsetProperty = Shape.StrokeDashOffsetProperty.AddOwner( + typeof(MapOverlay), new FrameworkPropertyMetadata((o, e) => ((MapOverlay)o).pen = null)); + + public static readonly DependencyProperty StrokeDashCapProperty = Shape.StrokeDashCapProperty.AddOwner( + typeof(MapOverlay), new FrameworkPropertyMetadata((o, e) => ((MapOverlay)o).pen = null)); + + public static readonly DependencyProperty StrokeStartLineCapProperty = Shape.StrokeStartLineCapProperty.AddOwner( + typeof(MapOverlay), new FrameworkPropertyMetadata((o, e) => ((MapOverlay)o).pen = null)); + + public static readonly DependencyProperty StrokeEndLineCapProperty = Shape.StrokeEndLineCapProperty.AddOwner( + typeof(MapOverlay), new FrameworkPropertyMetadata((o, e) => ((MapOverlay)o).pen = null)); + + public static readonly DependencyProperty StrokeLineJoinProperty = Shape.StrokeLineJoinProperty.AddOwner( + typeof(MapOverlay), new FrameworkPropertyMetadata((o, e) => ((MapOverlay)o).pen = null)); + + public static readonly DependencyProperty StrokeMiterLimitProperty = Shape.StrokeMiterLimitProperty.AddOwner( + typeof(MapOverlay), new FrameworkPropertyMetadata((o, e) => ((MapOverlay)o).pen = null)); + + private Typeface typeface; + private Pen pen; + + protected Typeface Typeface + { + get + { + if (typeface == null) + { + typeface = new Typeface(FontFamily, FontStyle, FontWeight, FontStretch); + } + + return typeface; + } + } + + protected Pen Pen + { + get + { + if (pen == null) + { + pen = new Pen + { + Brush = Stroke ?? Foreground, + Thickness = StrokeThickness, + DashStyle = new DashStyle(StrokeDashArray, StrokeDashOffset), + DashCap = StrokeDashCap, + StartLineCap = StrokeStartLineCap, + EndLineCap = StrokeEndLineCap, + LineJoin = StrokeLineJoin, + MiterLimit = StrokeMiterLimit + }; + + pen.Freeze(); + } + + return pen; + } + } + } +} diff --git a/MapControl/MapOverlay.cs b/MapControl/MapOverlay.cs index 88f1ad29..9e853ca6 100644 --- a/MapControl/MapOverlay.cs +++ b/MapControl/MapOverlay.cs @@ -2,72 +2,21 @@ // Copyright © 2014 Clemens Fischer // Licensed under the Microsoft Public License (Ms-PL) -using System; +#if WINDOWS_RUNTIME +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 { /// /// Base class for map overlays with font, background, foreground and stroke properties. - /// Rendering is typically done by overriding OnRender in derived classes. /// - public class MapOverlay : FrameworkElement, IMapElement + public partial class MapOverlay : MapPanel { - 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 BackgroundProperty = Control.BackgroundProperty.AddOwner( - typeof(MapOverlay)); - - public static readonly DependencyProperty ForegroundProperty = Control.ForegroundProperty.AddOwner( - typeof(MapOverlay), new FrameworkPropertyMetadata((o, e) => ((MapOverlay)o).ForegroundChanged())); - - public static readonly DependencyProperty StrokeProperty = Shape.StrokeProperty.AddOwner( - typeof(MapOverlay), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender, (o, e) => ((MapOverlay)o).pen = null)); - - public static readonly DependencyProperty StrokeThicknessProperty = Shape.StrokeThicknessProperty.AddOwner( - typeof(MapOverlay), new FrameworkPropertyMetadata(1d, FrameworkPropertyMetadataOptions.AffectsRender, (o, e) => ((MapOverlay)o).pen = null)); - - public static readonly DependencyProperty StrokeDashArrayProperty = Shape.StrokeDashArrayProperty.AddOwner( - typeof(MapOverlay), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender, (o, e) => ((MapOverlay)o).pen = null)); - - public static readonly DependencyProperty StrokeDashOffsetProperty = Shape.StrokeDashOffsetProperty.AddOwner( - typeof(MapOverlay), new FrameworkPropertyMetadata(0d, FrameworkPropertyMetadataOptions.AffectsRender, (o, e) => ((MapOverlay)o).pen = null)); - - public static readonly DependencyProperty StrokeDashCapProperty = Shape.StrokeDashCapProperty.AddOwner( - typeof(MapOverlay), new FrameworkPropertyMetadata(default(PenLineCap), FrameworkPropertyMetadataOptions.AffectsRender, (o, e) => ((MapOverlay)o).pen = null)); - - public static readonly DependencyProperty StrokeStartLineCapProperty = Shape.StrokeStartLineCapProperty.AddOwner( - typeof(MapOverlay), new FrameworkPropertyMetadata(default(PenLineCap), FrameworkPropertyMetadataOptions.AffectsRender, (o, e) => ((MapOverlay)o).pen = null)); - - public static readonly DependencyProperty StrokeEndLineCapProperty = Shape.StrokeEndLineCapProperty.AddOwner( - typeof(MapOverlay), new FrameworkPropertyMetadata(default(PenLineCap), FrameworkPropertyMetadataOptions.AffectsRender, (o, e) => ((MapOverlay)o).pen = null)); - - public static readonly DependencyProperty StrokeLineJoinProperty = Shape.StrokeLineJoinProperty.AddOwner( - typeof(MapOverlay), new FrameworkPropertyMetadata(default(PenLineJoin), FrameworkPropertyMetadataOptions.AffectsRender, (o, e) => ((MapOverlay)o).pen = null)); - - public static readonly DependencyProperty StrokeMiterLimitProperty = Shape.StrokeMiterLimitProperty.AddOwner( - typeof(MapOverlay), new FrameworkPropertyMetadata(1d, FrameworkPropertyMetadataOptions.AffectsRender, (o, e) => ((MapOverlay)o).pen = null)); - - private MapBase parentMap; - private Typeface typeface; - private Pen pen; - public double FontSize { get { return (double)GetValue(FontSizeProperty); } @@ -98,12 +47,6 @@ namespace MapControl set { SetValue(FontWeightProperty, value); } } - public Brush Background - { - get { return (Brush)GetValue(BackgroundProperty); } - set { SetValue(BackgroundProperty, value); } - } - public Brush Foreground { get { return (Brush)GetValue(ForegroundProperty); } @@ -163,80 +106,5 @@ namespace MapControl get { return (double)GetValue(StrokeMiterLimitProperty); } set { SetValue(StrokeMiterLimitProperty, value); } } - - public MapBase ParentMap - { - get { return parentMap; } - set - { - if (parentMap != null) - { - parentMap.ViewportChanged -= OnViewportChanged; - } - - parentMap = value; - - if (parentMap != null) - { - parentMap.ViewportChanged += OnViewportChanged; - OnViewportChanged(); - } - } - } - - protected Typeface Typeface - { - get - { - if (typeface == null) - { - typeface = new Typeface(FontFamily, FontStyle, FontWeight, FontStretch); - } - - return typeface; - } - } - - protected Pen Pen - { - get - { - if (pen == null) - { - pen = new Pen - { - Brush = Stroke ?? Foreground, - Thickness = StrokeThickness, - DashStyle = new DashStyle(StrokeDashArray, StrokeDashOffset), - DashCap = StrokeDashCap, - StartLineCap = StrokeStartLineCap, - EndLineCap = StrokeEndLineCap, - LineJoin = StrokeLineJoin, - MiterLimit = StrokeMiterLimit - }; - - pen.Freeze(); - } - - return pen; - } - } - - protected virtual void OnViewportChanged() - { - } - - private void OnViewportChanged(object sender, EventArgs e) - { - OnViewportChanged(); - } - - private void ForegroundChanged() - { - if (Stroke == null) - { - pen = null; - } - } } } diff --git a/MapControl/MapPanel.Silverlight.WinRT.cs b/MapControl/MapPanel.Silverlight.WinRT.cs index c7bf1e30..3f261587 100644 --- a/MapControl/MapPanel.Silverlight.WinRT.cs +++ b/MapControl/MapPanel.Silverlight.WinRT.cs @@ -2,7 +2,7 @@ // Copyright © 2014 Clemens Fischer // Licensed under the Microsoft Public License (Ms-PL) -#if NETFX_CORE +#if WINDOWS_RUNTIME using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Media; @@ -27,15 +27,10 @@ namespace MapControl } } - private UIElementCollection InternalChildren - { - get { return Children; } - } - /// - /// Helper method to work around missing property value inheritance in Silverlight and WinRT. - /// Adds Loaded and Unloaded handlers to the specified FrameworkElement, which set and clear - /// the value of the MapPanel.ParentMap attached property. + /// Helper method to work around missing property value inheritance in Silverlight and Windows Runtime. + /// Adds Loaded and Unloaded handlers to the specified FrameworkElement, which set and clear the value + /// of the MapPanel.ParentMap attached property. /// public static void AddParentMapHandlers(FrameworkElement element) { diff --git a/MapControl/MapPanel.cs b/MapControl/MapPanel.cs index 94193a69..e8726f57 100644 --- a/MapControl/MapPanel.cs +++ b/MapControl/MapPanel.cs @@ -3,30 +3,23 @@ // Licensed under the Microsoft Public License (Ms-PL) using System; -#if NETFX_CORE +#if WINDOWS_RUNTIME 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 { - public interface IMapElement - { - MapBase ParentMap { get; set; } - } - /// /// Positions child elements on a Map, at a position specified by the attached property Location. /// The Location is transformed to a viewport position by ParentMap.MapTransform and ParentMap.ViewportTransform /// and applied to a child element's RenderTransform as an appropriate TranslateTransform. /// - public partial class MapPanel : Panel, IMapElement + public partial class MapPanel : PanelBase, IMapElement { public static readonly DependencyProperty LocationProperty = DependencyProperty.RegisterAttached( "Location", typeof(Location), typeof(MapPanel), new PropertyMetadata(null, LocationPropertyChanged)); @@ -51,7 +44,7 @@ namespace MapControl private MapBase parentMap; - public MapBase ParentMap + public virtual MapBase ParentMap { get { return parentMap; } set @@ -71,18 +64,6 @@ namespace MapControl } } - protected override Size MeasureOverride(Size availableSize) - { - availableSize = new Size(double.PositiveInfinity, double.PositiveInfinity); - - foreach (UIElement element in InternalChildren) - { - element.Measure(availableSize); - } - - return new Size(); - } - protected override Size ArrangeOverride(Size finalSize) { foreach (UIElement element in InternalChildren) @@ -147,8 +128,7 @@ namespace MapControl } else if (e.OldValue == null) { - // Arrange element once when Location was null before - ArrangeElementWithLocation(element); + ArrangeElementWithLocation(element); // arrange element when Location was null before } SetViewportPosition(element, parentMap, location); @@ -161,8 +141,7 @@ namespace MapControl if (parentMap != null && location != null) { - // Keep ViewportPosition near map center - var mapPosition = parentMap.MapTransform.Transform(location, parentMap.Center.Longitude); + var mapPosition = parentMap.MapTransform.Transform(location, parentMap.Center.Longitude); // nearest to center longitude viewportPosition = parentMap.ViewportTransform.Transform(mapPosition); element.SetValue(ViewportPositionProperty, viewportPosition); } diff --git a/MapControl/MapPath.Silverlight.WinRT.cs b/MapControl/MapPath.Silverlight.WinRT.cs index 839bd653..37c59a0f 100644 --- a/MapControl/MapPath.Silverlight.WinRT.cs +++ b/MapControl/MapPath.Silverlight.WinRT.cs @@ -2,7 +2,7 @@ // Copyright © 2014 Clemens Fischer // Licensed under the Microsoft Public License (Ms-PL) -#if NETFX_CORE +#if WINDOWS_RUNTIME using Windows.UI.Xaml.Shapes; #else using System.Windows.Shapes; diff --git a/MapControl/MapPath.cs b/MapControl/MapPath.cs index 587591fb..665a2101 100644 --- a/MapControl/MapPath.cs +++ b/MapControl/MapPath.cs @@ -2,7 +2,7 @@ // Copyright © 2014 Clemens Fischer // Licensed under the Microsoft Public License (Ms-PL) -#if NETFX_CORE +#if WINDOWS_RUNTIME using Windows.Foundation; #else using System.Windows; @@ -33,7 +33,7 @@ namespace MapControl protected override Size MeasureOverride(Size constraint) { - // base.MeasureOverride in WPF and WinRT sometimes return a Size with zero + // base.MeasureOverride in WPF and Windows Runtime sometimes return a Size with zero // width or height, whereas base.MeasureOverride in Silverlight occasionally // throws an ArgumentException, as it tries to create a Size from a negative // width or height, apparently resulting from a transformed Geometry. diff --git a/MapControl/MapPolyline.Silverlight.WinRT.cs b/MapControl/MapPolyline.Silverlight.WinRT.cs index f6136200..d921ec58 100644 --- a/MapControl/MapPolyline.Silverlight.WinRT.cs +++ b/MapControl/MapPolyline.Silverlight.WinRT.cs @@ -3,7 +3,7 @@ // Licensed under the Microsoft Public License (Ms-PL) using System.Linq; -#if NETFX_CORE +#if WINDOWS_RUNTIME using Windows.UI.Xaml.Media; #else using System.Windows.Media; diff --git a/MapControl/MapPolyline.cs b/MapControl/MapPolyline.cs index 4fd940b7..a8fb4dc1 100644 --- a/MapControl/MapPolyline.cs +++ b/MapControl/MapPolyline.cs @@ -5,7 +5,7 @@ using System; using System.Collections.Generic; using System.Collections.Specialized; -#if NETFX_CORE +#if WINDOWS_RUNTIME using Windows.Foundation; using Windows.UI.Xaml; #else @@ -20,8 +20,8 @@ namespace MapControl /// public partial class MapPolyline : MapPath { -#if NETFX_CORE - // For WinRT, the Locations dependency property type is declared as object +#if WINDOWS_RUNTIME + // For Windows Runtime, the Locations dependency property type is declared as object // instead of IEnumerable. See http://stackoverflow.com/q/10544084/1136211 private static readonly Type LocationsPropertyType = typeof(object); #else @@ -38,7 +38,7 @@ namespace MapControl /// /// Gets or sets the locations that define the polyline points. /// -#if !NETFX_CORE +#if !WINDOWS_RUNTIME [TypeConverter(typeof(LocationCollectionConverter))] #endif public IEnumerable Locations diff --git a/MapControl/MapRectangle.WPF.cs b/MapControl/MapRectangle.WPF.cs deleted file mode 100644 index 73f2b630..00000000 --- a/MapControl/MapRectangle.WPF.cs +++ /dev/null @@ -1,14 +0,0 @@ -// XAML Map Control - http://xamlmapcontrol.codeplex.com/ -// Copyright © 2014 Clemens Fischer -// Licensed under the Microsoft Public License (Ms-PL) - -namespace MapControl -{ - public partial class MapRectangle - { - static MapRectangle() - { - geometryScaleTransform.Freeze(); - } - } -} diff --git a/MapControl/MapRectangle.cs b/MapControl/MapRectangle.cs index 2440d594..79baf632 100644 --- a/MapControl/MapRectangle.cs +++ b/MapControl/MapRectangle.cs @@ -2,7 +2,7 @@ // Copyright © 2014 Clemens Fischer // Licensed under the Microsoft Public License (Ms-PL) -#if NETFX_CORE +#if WINDOWS_RUNTIME using Windows.Foundation; using Windows.UI.Xaml; using Windows.UI.Xaml.Media; @@ -16,16 +16,8 @@ namespace MapControl /// /// Fills a rectangular area defined by South, North, West and East with a Brush. /// - public partial class MapRectangle : MapPath + public class MapRectangle : MapPath { - private const double GeometryScale = 1e6; - - private static readonly ScaleTransform geometryScaleTransform = new ScaleTransform - { - ScaleX = 1d / GeometryScale, - ScaleY = 1d / GeometryScale - }; - public static readonly DependencyProperty SouthProperty = DependencyProperty.Register( "South", typeof(double), typeof(MapRectangle), new PropertyMetadata(double.NaN, (o, e) => ((MapRectangle)o).UpdateData())); @@ -81,17 +73,23 @@ namespace MapControl !double.IsNaN(West) && !double.IsNaN(East) && South < North && West < East) { - var p1 = ParentMap.MapTransform.Transform(new Location(South, West)); - var p2 = ParentMap.MapTransform.Transform(new Location(North, East)); - // Create a scaled RectangleGeometry due to inaccurate hit testing in WPF. // See http://stackoverflow.com/a/19335624/1136211 - geometry.Rect = new Rect(p1.X * GeometryScale, p1.Y * GeometryScale, - (p2.X - p1.X) * GeometryScale, (p2.Y - p1.Y) * GeometryScale); + const double scale = 1e6; + var p1 = ParentMap.MapTransform.Transform(new Location(South, West)); + var p2 = ParentMap.MapTransform.Transform(new Location(North, East)); + geometry.Rect = new Rect(p1.X * scale, p1.Y * scale, (p2.X - p1.X) * scale, (p2.Y - p1.Y) * scale); + + var scaleTransform = new ScaleTransform // revert scaling + { + ScaleX = 1d / scale, + ScaleY = 1d / scale + }; + scaleTransform.Freeze(); var transform = new TransformGroup(); - transform.Children.Add(geometryScaleTransform); // revert scaling + transform.Children.Add(scaleTransform); transform.Children.Add(ParentMap.ViewportTransform); RenderTransform = transform; } diff --git a/MapControl/MapScale.cs b/MapControl/MapScale.cs index d94f8dca..2b6428e4 100644 --- a/MapControl/MapScale.cs +++ b/MapControl/MapScale.cs @@ -93,7 +93,9 @@ namespace MapControl 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))); + GlyphRunText.Create(text, Typeface, FontSize), + new Point(size.Width / 2d, y1 - StrokeThickness - 1d), + HorizontalAlignment.Center, VerticalAlignment.Center); } } diff --git a/MapControl/MapTransform.cs b/MapControl/MapTransform.cs index 93077598..03ac5854 100644 --- a/MapControl/MapTransform.cs +++ b/MapControl/MapTransform.cs @@ -2,7 +2,7 @@ // Copyright © 2014 Clemens Fischer // Licensed under the Microsoft Public License (Ms-PL) -#if NETFX_CORE +#if WINDOWS_RUNTIME using Windows.Foundation; #else using System.Windows; @@ -30,16 +30,16 @@ namespace MapControl /// public abstract double RelativeScale(Location location); - /// - /// Transforms a geographic location to a cartesian coordinate point. - /// - public abstract Point Transform(Location location); - /// /// Transforms a cartesian coordinate point to a geographic location. /// public abstract Location Transform(Point point); + /// + /// Transforms a geographic location to a cartesian coordinate point. + /// + public abstract Point Transform(Location location); + /// /// Transforms a geographic location to a cartesian coordinate point /// with minumum distance to the specified reference longitude value. diff --git a/MapControl/MercatorTransform.cs b/MapControl/MercatorTransform.cs index db2202e5..4c4b558f 100644 --- a/MapControl/MercatorTransform.cs +++ b/MapControl/MercatorTransform.cs @@ -3,7 +3,7 @@ // Licensed under the Microsoft Public License (Ms-PL) using System; -#if NETFX_CORE +#if WINDOWS_RUNTIME using Windows.Foundation; #else using System.Windows; diff --git a/MapControl/PanelBase.cs b/MapControl/PanelBase.cs new file mode 100644 index 00000000..063a9623 --- /dev/null +++ b/MapControl/PanelBase.cs @@ -0,0 +1,50 @@ +// XAML Map Control - http://xamlmapcontrol.codeplex.com/ +// Copyright © 2014 Clemens Fischer +// Licensed under the Microsoft Public License (Ms-PL) + +#if WINDOWS_RUNTIME +using Windows.Foundation; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +#else +using System.Windows; +using System.Windows.Controls; + +#endif + +namespace MapControl +{ + /// + /// Common base class for MapPanel, TileLayer and TileContainer. + /// + public class PanelBase : Panel + { +#if WINDOWS_RUNTIME || SILVERLIGHT + protected internal UIElementCollection InternalChildren + { + get { return Children; } + } +#endif + protected override Size MeasureOverride(Size availableSize) + { + availableSize = new Size(double.PositiveInfinity, double.PositiveInfinity); + + foreach (UIElement element in InternalChildren) + { + element.Measure(availableSize); + } + + return new Size(); + } + + protected override Size ArrangeOverride(Size finalSize) + { + foreach (UIElement child in InternalChildren) + { + child.Arrange(new Rect(new Point(), finalSize)); + } + + return finalSize; + } + } +} diff --git a/MapControl/Properties/AssemblyInfo.cs b/MapControl/Properties/AssemblyInfo.cs index ada7175a..1eda3a64 100644 --- a/MapControl/Properties/AssemblyInfo.cs +++ b/MapControl/Properties/AssemblyInfo.cs @@ -10,13 +10,12 @@ using System.Windows; [assembly: AssemblyDescription("XAML Map Control Library for WPF")] [assembly: ThemeInfo(ResourceDictionaryLocation.None, ResourceDictionaryLocation.SourceAssembly)] #endif - [assembly: AssemblyProduct("XAML Map Control")] [assembly: AssemblyCompany("Clemens Fischer")] [assembly: AssemblyCopyright("Copyright © 2014 Clemens Fischer")] [assembly: AssemblyTrademark("")] -[assembly: AssemblyVersion("1.12.1")] -[assembly: AssemblyFileVersion("1.12.1")] +[assembly: AssemblyVersion("2.0.0")] +[assembly: AssemblyFileVersion("2.0.0")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCulture("")] [assembly: ComVisible(false)] diff --git a/MapControl/Pushpin.Silverlight.WinRT.cs b/MapControl/Pushpin.Silverlight.WinRT.cs index 1325d99c..7f5279bb 100644 --- a/MapControl/Pushpin.Silverlight.WinRT.cs +++ b/MapControl/Pushpin.Silverlight.WinRT.cs @@ -2,7 +2,7 @@ // Copyright © 2014 Clemens Fischer // Licensed under the Microsoft Public License (Ms-PL) -#if NETFX_CORE +#if WINDOWS_RUNTIME using Windows.UI.Xaml.Controls; #else using System.Windows.Controls; diff --git a/MapControl/Tile.Silverlight.WinRT.cs b/MapControl/Tile.Silverlight.WinRT.cs index 1b10c377..82d1bafc 100644 --- a/MapControl/Tile.Silverlight.WinRT.cs +++ b/MapControl/Tile.Silverlight.WinRT.cs @@ -2,7 +2,7 @@ // Copyright © 2014 Clemens Fischer // Licensed under the Microsoft Public License (Ms-PL) -#if NETFX_CORE +#if WINDOWS_RUNTIME using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Media; @@ -20,13 +20,6 @@ namespace MapControl { public partial class Tile { - public readonly Image Image = new Image { Opacity = 0d }; - - public ImageSource ImageSource - { - get { return Image.Source; } - } - public void SetImageSource(ImageSource image, bool animateOpacity) { if (image != null && Image.Source == null) diff --git a/MapControl/Tile.WPF.cs b/MapControl/Tile.WPF.cs index 2a847361..e3706e85 100644 --- a/MapControl/Tile.WPF.cs +++ b/MapControl/Tile.WPF.cs @@ -3,6 +3,7 @@ // Licensed under the Microsoft Public License (Ms-PL) using System; +using System.Windows.Controls; using System.Windows.Media; using System.Windows.Media.Animation; using System.Windows.Media.Imaging; @@ -11,16 +12,9 @@ namespace MapControl { public partial class Tile { - public readonly ImageBrush Brush = new ImageBrush { Opacity = 0d }; - - public ImageSource ImageSource - { - get { return Brush.ImageSource; } - } - public void SetImageSource(ImageSource image, bool animateOpacity) { - if (image != null && Brush.ImageSource == null) + if (image != null && Image.Source == null) { if (animateOpacity) { @@ -33,16 +27,16 @@ namespace MapControl } else { - Brush.BeginAnimation(ImageBrush.OpacityProperty, new DoubleAnimation(1d, AnimationDuration)); + Image.BeginAnimation(Image.OpacityProperty, new DoubleAnimation(1d, AnimationDuration)); } } else { - Brush.Opacity = 1d; + Image.Opacity = 1d; } } - Brush.ImageSource = image; + Image.Source = image; HasImageSource = true; } @@ -53,7 +47,7 @@ namespace MapControl bitmap.DownloadCompleted -= BitmapDownloadCompleted; bitmap.DownloadFailed -= BitmapDownloadFailed; - Brush.BeginAnimation(ImageBrush.OpacityProperty, new DoubleAnimation(1d, AnimationDuration)); + Image.BeginAnimation(Image.OpacityProperty, new DoubleAnimation(1d, AnimationDuration)); } private void BitmapDownloadFailed(object sender, ExceptionEventArgs e) @@ -63,7 +57,7 @@ namespace MapControl bitmap.DownloadCompleted -= BitmapDownloadCompleted; bitmap.DownloadFailed -= BitmapDownloadFailed; - Brush.ImageSource = null; + Image.Source = null; } } } diff --git a/MapControl/Tile.cs b/MapControl/Tile.cs index 02042f97..c5ab8a7f 100644 --- a/MapControl/Tile.cs +++ b/MapControl/Tile.cs @@ -3,6 +3,11 @@ // Licensed under the Microsoft Public License (Ms-PL) using System; +#if WINDOWS_RUNTIME +using Windows.UI.Xaml.Controls; +#else +using System.Windows.Controls; +#endif namespace MapControl { @@ -13,6 +18,7 @@ namespace MapControl public readonly int ZoomLevel; public readonly int X; public readonly int Y; + public readonly Image Image = new Image { Opacity = 0d }; public Tile(int zoomLevel, int x, int y) { diff --git a/MapControl/TileContainer.Silverlight.WinRT.cs b/MapControl/TileContainer.Silverlight.WinRT.cs index be95fd74..96372f2c 100644 --- a/MapControl/TileContainer.Silverlight.WinRT.cs +++ b/MapControl/TileContainer.Silverlight.WinRT.cs @@ -3,37 +3,19 @@ // Licensed under the Microsoft Public License (Ms-PL) using System; -#if NETFX_CORE -using Windows.Foundation; -using Windows.UI.Xaml.Controls; +#if WINDOWS_RUNTIME 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 + internal partial class TileContainer { private Matrix GetViewportTransformMatrix(double scale, double offsetX, double offsetY) { - var transform = new Matrix(scale, 0d, 0d, -scale, offsetX, offsetY); - - return transform.RotateAt(rotation, viewportOrigin.X, viewportOrigin.Y); - } - - /// - /// Gets a transform matrix with origin at tileGrid.X and tileGrid.Y to minimize rounding errors. - /// - private Matrix GetTileLayerTransformMatrix() - { - var scale = Math.Pow(2d, zoomLevel - tileZoomLevel); - - return new Matrix(1d, 0d, 0d, 1d, tileGrid.X * TileSource.TileSize, tileGrid.Y * TileSource.TileSize) - .Scale(scale, scale) - .Translate(tileLayerOffset.X, tileLayerOffset.Y) + return new Matrix(scale, 0d, 0d, -scale, offsetX, offsetY) .RotateAt(rotation, viewportOrigin.X, viewportOrigin.Y); } @@ -47,24 +29,18 @@ namespace MapControl .Scale(scale, -scale); // map coordinates to tile indices } - protected override Size MeasureOverride(Size availableSize) + /// + /// Sets a RenderTransform with origin at tileGrid.X and tileGrid.Y to minimize rounding errors. + /// + private void UpdateRenderTransform() { - foreach (TileLayer tileLayer in Children) - { - tileLayer.Measure(availableSize); - } + var scale = Math.Pow(2d, zoomLevel - tileZoomLevel); - return new Size(); - } - - protected override Size ArrangeOverride(Size finalSize) - { - foreach (TileLayer tileLayer in Children) - { - tileLayer.Arrange(new Rect()); - } - - return finalSize; + ((MatrixTransform)RenderTransform).Matrix = + new Matrix(1d, 0d, 0d, 1d, tileGrid.X * TileSource.TileSize, tileGrid.Y * TileSource.TileSize) + .Scale(scale, scale) + .Translate(tileLayerOffset.X, tileLayerOffset.Y) + .RotateAt(rotation, viewportOrigin.X, viewportOrigin.Y); } } } diff --git a/MapControl/TileContainer.WPF.cs b/MapControl/TileContainer.WPF.cs index 64eaba80..9dd5a348 100644 --- a/MapControl/TileContainer.WPF.cs +++ b/MapControl/TileContainer.WPF.cs @@ -7,27 +7,11 @@ using System.Windows.Media; namespace MapControl { - internal partial class TileContainer : ContainerVisual + internal partial class TileContainer { private Matrix GetViewportTransformMatrix(double scale, double offsetX, double offsetY) { var transform = new Matrix(scale, 0d, 0d, -scale, offsetX, offsetY); - - transform.RotateAt(rotation, viewportOrigin.X, viewportOrigin.Y); - - return transform; - } - - /// - /// Gets a transform matrix with origin at tileGrid.X and tileGrid.Y to minimize rounding errors. - /// - private Matrix GetTileLayerTransformMatrix() - { - var scale = Math.Pow(2d, zoomLevel - tileZoomLevel); - var transform = new Matrix(1d, 0d, 0d, 1d, tileGrid.X * TileSource.TileSize, tileGrid.Y * TileSource.TileSize); - - transform.Scale(scale, scale); - transform.Translate(tileLayerOffset.X, tileLayerOffset.Y); transform.RotateAt(rotation, viewportOrigin.X, viewportOrigin.Y); return transform; @@ -37,12 +21,25 @@ namespace MapControl { var scale = (double)numTiles / 360d; var transform = ViewportTransform.Matrix; - transform.Invert(); // view to map coordinates transform.Translate(180d, -180d); transform.Scale(scale, -scale); // map coordinates to tile indices return transform; } + + /// + /// Sets a RenderTransform with origin at tileGrid.X and tileGrid.Y to minimize rounding errors. + /// + private void UpdateRenderTransform() + { + var scale = Math.Pow(2d, zoomLevel - tileZoomLevel); + var transform = new Matrix(1d, 0d, 0d, 1d, tileGrid.X * TileSource.TileSize, tileGrid.Y * TileSource.TileSize); + transform.Scale(scale, scale); + transform.Translate(tileLayerOffset.X, tileLayerOffset.Y); + transform.RotateAt(rotation, viewportOrigin.X, viewportOrigin.Y); + + ((MatrixTransform)RenderTransform).Matrix = transform; + } } } diff --git a/MapControl/TileContainer.cs b/MapControl/TileContainer.cs index 1c61865e..e4080181 100644 --- a/MapControl/TileContainer.cs +++ b/MapControl/TileContainer.cs @@ -4,7 +4,8 @@ using System; using System.Collections.Generic; -#if NETFX_CORE +using System.Linq; +#if WINDOWS_RUNTIME using Windows.Foundation; using Windows.UI.Xaml; using Windows.UI.Xaml.Media; @@ -16,7 +17,7 @@ using System.Windows.Threading; namespace MapControl { - internal partial class TileContainer + internal partial class TileContainer : PanelBase { // relative scaled tile size ranges from 0.75 to 1.5 (192 to 384 pixels) private static double zoomLevelSwitchDelta = -Math.Log(0.75, 2d); @@ -36,27 +37,30 @@ namespace MapControl public TileContainer() { + RenderTransform = new MatrixTransform(); updateTimer = new DispatcherTimer { Interval = UpdateInterval }; updateTimer.Tick += UpdateTiles; } + public IEnumerable TileLayers + { + get { return InternalChildren.Cast(); } + } + public void AddTileLayers(int index, IEnumerable tileLayers) { - var tileLayerTransform = GetTileLayerTransformMatrix(); - foreach (var tileLayer in tileLayers) { - if (index < Children.Count) + if (index < InternalChildren.Count) { - Children.Insert(index, tileLayer); + InternalChildren.Insert(index, tileLayer); } else { - Children.Add(tileLayer); + InternalChildren.Add(tileLayer); } index++; - tileLayer.SetTransformMatrix(tileLayerTransform); tileLayer.UpdateTiles(tileZoomLevel, tileGrid); } } @@ -65,19 +69,19 @@ namespace MapControl { while (count-- > 0) { - ((TileLayer)Children[index]).ClearTiles(); - Children.RemoveAt(index); + ((TileLayer)InternalChildren[index]).ClearTiles(); + InternalChildren.RemoveAt(index); } } public void ClearTileLayers() { - foreach (TileLayer tileLayer in Children) + foreach (TileLayer tileLayer in InternalChildren) { tileLayer.ClearTiles(); } - Children.Clear(); + InternalChildren.Clear(); } public double SetViewportTransform(double mapZoomLevel, double mapRotation, Point mapOrigin, Point vpOrigin, Size vpSize) @@ -103,12 +107,7 @@ namespace MapControl tileLayerOffset.X = transformOffsetX - 180d * scale; tileLayerOffset.Y = transformOffsetY - 180d * scale; - var tileLayerTransform = GetTileLayerTransformMatrix(); - - foreach (TileLayer tileLayer in Children) - { - tileLayer.SetTransformMatrix(tileLayerTransform); - } + UpdateRenderTransform(); if (Math.Abs(mapOrigin.X - oldMapOriginX) > 180d) { @@ -147,11 +146,11 @@ namespace MapControl { tileZoomLevel = zoom; tileGrid = grid; - var tileLayerTransform = GetTileLayerTransformMatrix(); - foreach (TileLayer tileLayer in Children) + UpdateRenderTransform(); + + foreach (TileLayer tileLayer in InternalChildren) { - tileLayer.SetTransformMatrix(tileLayerTransform); tileLayer.UpdateTiles(tileZoomLevel, tileGrid); } } diff --git a/MapControl/TileImageLoader.Silverlight.WinRT.cs b/MapControl/TileImageLoader.Silverlight.WinRT.cs index b05b8364..9bc58a05 100644 --- a/MapControl/TileImageLoader.Silverlight.WinRT.cs +++ b/MapControl/TileImageLoader.Silverlight.WinRT.cs @@ -5,7 +5,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; -#if NETFX_CORE +#if WINDOWS_RUNTIME using Windows.UI.Xaml.Media; using Windows.UI.Xaml.Media.Imaging; #else @@ -24,33 +24,40 @@ namespace MapControl { var imageTileSource = tileLayer.TileSource as ImageTileSource; - foreach (var tile in tiles) + if (imageTileSource != null) { - try + foreach (var tile in tiles) { - ImageSource image; - - if (imageTileSource != null) + try { - image = imageTileSource.LoadImage(tile.XIndex, tile.Y, tile.ZoomLevel); + var image = imageTileSource.LoadImage(tile.XIndex, tile.Y, tile.ZoomLevel); + tile.SetImageSource(image, tileLayer.AnimateTileOpacity); } - else + catch (Exception ex) + { + Debug.WriteLine("Loading tile image failed: {0}", ex.Message); + } + } + } + else + { + foreach (var tile in tiles) + { + try { var uri = tileLayer.TileSource.GetUri(tile.XIndex, tile.Y, tile.ZoomLevel); - - image = uri != null ? new BitmapImage(uri) : null; + var image = uri != null ? new BitmapImage(uri) : null; + tile.SetImageSource(image, tileLayer.AnimateTileOpacity); + } + catch (Exception ex) + { + Debug.WriteLine("Creating tile image failed: {0}", ex.Message); } - - tile.SetImageSource(image, tileLayer.AnimateTileOpacity); - } - catch (Exception ex) - { - Debug.WriteLine("Creating tile image failed: {0}", ex.Message); } } } - internal void CancelGetTiles() + public void CancelGetTiles() { } } diff --git a/MapControl/TileImageLoader.WPF.cs b/MapControl/TileImageLoader.WPF.cs index 71d50119..16621882 100644 --- a/MapControl/TileImageLoader.WPF.cs +++ b/MapControl/TileImageLoader.WPF.cs @@ -34,7 +34,7 @@ namespace MapControl Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "MapControl"); /// - /// The ObjectCache used to cache tile images. The default is null. + /// The ObjectCache used to cache tile images. The default is MemoryCache.Default. /// public static ObjectCache Cache { get; set; } @@ -57,6 +57,7 @@ namespace MapControl static TileImageLoader() { + Cache = MemoryCache.Default; CacheExpiration = TimeSpan.FromDays(7); CacheUpdateAge = TimeSpan.FromDays(1); } @@ -66,19 +67,32 @@ namespace MapControl internal void BeginGetTiles(TileLayer tileLayer, IEnumerable tiles) { - var tileList = tiles.ToList(); - - if (tileList.Count > 0) + if (tiles.Any()) { // get current TileLayer property values in UI thread - var dispatcher = tileLayer.Dispatcher; var tileSource = tileLayer.TileSource; - var sourceName = tileLayer.SourceName; - var maxDownloads = tileLayer.MaxParallelDownloads; + var imageTileSource = tileSource as ImageTileSource; var animateOpacity = tileLayer.AnimateTileOpacity; + var dispatcher = tileLayer.Dispatcher; - ThreadPool.QueueUserWorkItem(o => - GetTiles(tileList, dispatcher, tileSource, sourceName, maxDownloads, animateOpacity)); + if (imageTileSource != null && !imageTileSource.IsAsync) // call LoadImage in UI thread + { + var setImageAction = new Action(t => t.SetImageSource(LoadImage(imageTileSource, t), animateOpacity)); + + foreach (var tile in tiles) + { + dispatcher.BeginInvoke(setImageAction, DispatcherPriority.Background, tile); // with low priority + } + } + else + { + var tileList = tiles.ToList(); + var sourceName = tileLayer.SourceName; + var maxDownloads = tileLayer.MaxParallelDownloads; + + ThreadPool.QueueUserWorkItem(o => + GetTiles(tileList, dispatcher, tileSource, sourceName, animateOpacity, maxDownloads)); + } } } @@ -88,69 +102,35 @@ namespace MapControl while (pendingTiles.TryDequeue(out tile)) ; // no Clear method } - private void GetTiles(List tiles, Dispatcher dispatcher, TileSource tileSource, - string sourceName, int maxDownloads, bool animateOpacity) + private void GetTiles(List tiles, Dispatcher dispatcher, TileSource tileSource, string sourceName, bool animateOpacity, int maxDownloads) { - var imageTileSource = tileSource as ImageTileSource; - - if (imageTileSource != null) + if (Cache != null && !string.IsNullOrWhiteSpace(sourceName) && + !(tileSource is ImageTileSource) && !tileSource.UriFormat.StartsWith("file:")) { - if (!imageTileSource.CanLoadAsync) // call LoadImage in UI thread + var setImageAction = new Action((t, i) => t.SetImageSource(i, animateOpacity)); + var outdatedTiles = new List(tiles.Count); + + foreach (var tile in tiles) { - var setImageAction = new Action((t, ts) => t.SetImageSource(LoadImage(ts, t), animateOpacity)); + var buffer = Cache.Get(TileCache.Key(sourceName, tile)) as byte[]; + var image = CreateImage(buffer); - foreach (var tile in tiles) + if (image == null) { - dispatcher.BeginInvoke(setImageAction, tile, imageTileSource); + pendingTiles.Enqueue(tile); // not yet cached } - - return; - } - } - else if (!tileSource.UriFormat.StartsWith("file:")) // load local image files asynchronously, without caching - { - if (Cache == null || string.IsNullOrWhiteSpace(sourceName)) - { - // no caching here: use default asynchronous downloading and caching done by WPF - - var setImageAction = new Action((t, ts) => t.SetImageSource(CreateImage(ts, t), animateOpacity)); - - foreach (var tile in tiles) + else if (CacheUpdateAge > TimeSpan.Zero && TileCache.CreationTime(buffer) + CacheUpdateAge < DateTime.UtcNow) { - dispatcher.BeginInvoke(setImageAction, tile, tileSource); + dispatcher.Invoke(setImageAction, tile, image); // synchronously before enqueuing + outdatedTiles.Add(tile); // update outdated cache } - - return; - } - else - { - var setImageAction = new Action((t, i) => t.SetImageSource(i, animateOpacity)); - var outdatedTiles = new List(tiles.Count); - - foreach (var tile in tiles) + else { - var key = GetCacheKey(sourceName, tile); - var buffer = Cache.Get(key) as byte[]; - var image = CreateImage(buffer); - - if (image == null) - { - pendingTiles.Enqueue(tile); // not yet cached - } - else if (CacheUpdateAge > TimeSpan.Zero && - DateTime.FromBinary(BitConverter.ToInt64(buffer, 0)) + CacheUpdateAge < DateTime.UtcNow) - { - dispatcher.Invoke(setImageAction, tile, image); // synchronously before enqueuing - outdatedTiles.Add(tile); // update outdated cache - } - else - { - dispatcher.BeginInvoke(setImageAction, tile, image); - } + dispatcher.BeginInvoke(setImageAction, tile, image); } - - tiles = outdatedTiles; // enqueue outdated tiles at last } + + tiles = outdatedTiles; // enqueue outdated tiles after current tiles } foreach (var tile in tiles) @@ -187,7 +167,7 @@ namespace MapControl if (uri != null) { - if (uri.Scheme == "file") // create from FileStream as creating from URI leaves the file open + if (uri.Scheme == "file") // create from FileStream because creating from URI leaves the file open { image = CreateImage(uri.LocalPath); } @@ -199,25 +179,20 @@ namespace MapControl } } - if (image != null || !tile.HasImageSource) // do not set null if tile already has an image (from cache) + if (image != null || !tile.HasImageSource) // set null image if tile does not yet have an ImageSource { dispatcher.BeginInvoke(setImageAction, tile, image); } - if (buffer != null && image != null) + if (image != null && buffer != null && Cache != null && !string.IsNullOrWhiteSpace(sourceName)) { - Cache.Set(GetCacheKey(sourceName, tile), buffer, new CacheItemPolicy { SlidingExpiration = CacheExpiration }); + Cache.Set(TileCache.Key(sourceName, tile), buffer, new CacheItemPolicy { SlidingExpiration = CacheExpiration }); } } Interlocked.Decrement(ref threadCount); } - private static string GetCacheKey(string sourceName, Tile tile) - { - return string.Format("{0}/{1}/{2}/{3}", sourceName, tile.ZoomLevel, tile.XIndex, tile.Y); - } - private static ImageSource LoadImage(ImageTileSource tileSource, Tile tile) { ImageSource image = null; @@ -234,27 +209,6 @@ namespace MapControl return image; } - private static ImageSource CreateImage(TileSource tileSource, Tile tile) - { - ImageSource image = null; - - try - { - var uri = tileSource.GetUri(tile.XIndex, tile.Y, tile.ZoomLevel); - - if (uri != null) - { - image = BitmapFrame.Create(uri); - } - } - catch (Exception ex) - { - Trace.TraceWarning("Creating tile image failed: {0}", ex.Message); - } - - return image; - } - private static ImageSource CreateImage(string path) { ImageSource image = null; @@ -281,11 +235,11 @@ namespace MapControl { ImageSource image = null; - if (buffer != null && buffer.Length > sizeof(long)) + if (buffer != null) { try { - using (var stream = new MemoryStream(buffer, sizeof(long), buffer.Length - sizeof(long), false)) + using (var stream = TileCache.ImageStream(buffer)) { image = BitmapFrame.Create(stream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad); } @@ -311,17 +265,7 @@ namespace MapControl using (var response = (HttpWebResponse)request.GetResponse()) using (var responseStream = response.GetResponseStream()) { - var length = (int)response.ContentLength; - - using (var memoryStream = length > 0 ? new MemoryStream(length + sizeof(long)) : new MemoryStream()) - { - var creationTime = DateTime.UtcNow.ToBinary(); - - memoryStream.Write(BitConverter.GetBytes(creationTime), 0, sizeof(long)); - responseStream.CopyTo(memoryStream); - - buffer = length > 0 ? memoryStream.GetBuffer() : memoryStream.ToArray(); - } + buffer = TileCache.CreateBuffer(responseStream, (int)response.ContentLength); } } catch (WebException ex) @@ -346,5 +290,38 @@ namespace MapControl return buffer; } + + private static class TileCache + { + private const int imageBufferOffset = sizeof(Int64); + + public static string Key(string sourceName, Tile tile) + { + return string.Format("{0}/{1}/{2}/{3}", sourceName, tile.ZoomLevel, tile.XIndex, tile.Y); + } + + public static MemoryStream ImageStream(byte[] cacheBuffer) + { + return new MemoryStream(cacheBuffer, imageBufferOffset, cacheBuffer.Length - imageBufferOffset, false); + } + + public static DateTime CreationTime(byte[] cacheBuffer) + { + return DateTime.FromBinary(BitConverter.ToInt64(cacheBuffer, 0)); + } + + public static byte[] CreateBuffer(Stream imageStream, int length) + { + var creationTime = BitConverter.GetBytes(DateTime.UtcNow.ToBinary()); + + using (var memoryStream = length > 0 ? new MemoryStream(length + imageBufferOffset) : new MemoryStream()) + { + memoryStream.Write(creationTime, 0, imageBufferOffset); + imageStream.CopyTo(memoryStream); + + return length > 0 ? memoryStream.GetBuffer() : memoryStream.ToArray(); + } + } + } } } diff --git a/MapControl/TileLayer.Silverlight.WinRT.cs b/MapControl/TileLayer.Silverlight.WinRT.cs deleted file mode 100644 index 154ee159..00000000 --- a/MapControl/TileLayer.Silverlight.WinRT.cs +++ /dev/null @@ -1,60 +0,0 @@ -// XAML Map Control - http://xamlmapcontrol.codeplex.com/ -// Copyright © 2014 Clemens Fischer -// Licensed under the Microsoft Public License (Ms-PL) - -#if NETFX_CORE -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; - } - } -} diff --git a/MapControl/TileLayer.WPF.cs b/MapControl/TileLayer.WPF.cs deleted file mode 100644 index cc27909c..00000000 --- a/MapControl/TileLayer.WPF.cs +++ /dev/null @@ -1,46 +0,0 @@ -// XAML Map Control - http://xamlmapcontrol.codeplex.com/ -// Copyright © 2014 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; - } - - public Brush Background { get; set; } - - private ContainerVisual TileContainer - { - get { return Parent as ContainerVisual; } - } - - private void RenderTiles() - { - using (var drawingContext = RenderOpen()) - { - foreach (var tile in tiles) - { - var tileSize = TileSource.TileSize << (zoomLevel - tile.ZoomLevel); - var tileRect = new Rect( - tileSize * tile.X - TileSource.TileSize * grid.X, - tileSize * tile.Y - TileSource.TileSize * 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); - } - } - } - } -} diff --git a/MapControl/TileLayer.cs b/MapControl/TileLayer.cs index e393a18d..2b623f2c 100644 --- a/MapControl/TileLayer.cs +++ b/MapControl/TileLayer.cs @@ -5,7 +5,8 @@ using System; using System.Collections.Generic; using System.Linq; -#if NETFX_CORE +#if WINDOWS_RUNTIME +using Windows.Foundation; using Windows.UI.Xaml.Markup; using Windows.UI.Xaml.Media; #else @@ -19,12 +20,12 @@ namespace MapControl /// /// Fills a rectangular area with map tiles from a TileSource. /// -#if NETFX_CORE +#if WINDOWS_RUNTIME [ContentProperty(Name = "TileSource")] #else [ContentProperty("TileSource")] #endif - public partial class TileLayer + public class TileLayer : PanelBase { public static TileLayer Default { @@ -39,7 +40,6 @@ namespace MapControl } } - private readonly MatrixTransform transform = new MatrixTransform(); private readonly TileImageLoader tileImageLoader = new TileImageLoader(); private string description = string.Empty; private TileSource tileSource; @@ -54,11 +54,8 @@ namespace MapControl MaxParallelDownloads = 8; LoadLowerZoomLevels = true; AnimateTileOpacity = true; - Initialize(); } - partial void Initialize(); - public string SourceName { get; set; } public int MinZoomLevel { get; set; } public int MaxZoomLevel { get; set; } @@ -99,26 +96,14 @@ namespace MapControl } } - public string TileSourceUriFormat - { - get { return tileSource != null ? tileSource.UriFormat : string.Empty; } - set { TileSource = !string.IsNullOrWhiteSpace(value) ? new TileSource(value) : null; } - } - - internal void SetTransformMatrix(Matrix transformMatrix) - { - transform.Matrix = transformMatrix; - } - internal void UpdateTiles(int zoomLevel, Int32Rect grid) { this.grid = grid; this.zoomLevel = zoomLevel; - tileImageLoader.CancelGetTiles(); - if (tileSource != null) { + tileImageLoader.CancelGetTiles(); SelectTiles(); RenderTiles(); tileImageLoader.BeginGetTiles(this, tiles.Where(t => !t.HasImageSource)); @@ -136,9 +121,8 @@ namespace MapControl { var maxZoomLevel = Math.Min(zoomLevel, MaxZoomLevel); var minZoomLevel = maxZoomLevel; - var container = TileContainer; - if (LoadLowerZoomLevels && container != null && container.Children.IndexOf(this) == 0) + if (LoadLowerZoomLevels && Parent is TileContainer && ((TileContainer)Parent).TileLayers.FirstOrDefault() == this) { minZoomLevel = MinZoomLevel; } @@ -164,12 +148,12 @@ namespace MapControl tile = new Tile(z, x, y); var equivalentTile = tiles.FirstOrDefault( - t => t.ImageSource != null && t.ZoomLevel == z && t.XIndex == tile.XIndex && t.Y == y); + t => t.Image.Source != null && t.ZoomLevel == z && t.XIndex == tile.XIndex && t.Y == y); if (equivalentTile != null) { // do not animate to avoid flicker when crossing date line - tile.SetImageSource(equivalentTile.ImageSource, false); + tile.SetImageSource(equivalentTile.Image.Source, false); } } @@ -180,5 +164,28 @@ namespace MapControl tiles = newTiles; } + + private void RenderTiles() + { + InternalChildren.Clear(); + + foreach (var tile in tiles) + { + InternalChildren.Add(tile.Image); + } + } + + 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; + } } } diff --git a/MapControl/TileSource.cs b/MapControl/TileSource.cs index 8a790b41..97d0da98 100644 --- a/MapControl/TileSource.cs +++ b/MapControl/TileSource.cs @@ -5,7 +5,7 @@ using System; using System.Globalization; using System.Text; -#if NETFX_CORE +#if WINDOWS_RUNTIME using Windows.Foundation; #else using System.Windows; @@ -94,7 +94,7 @@ namespace MapControl private Uri GetBasicUri(int x, int y, int zoomLevel) { - return new Uri(UriFormat. + return new Uri(uriFormat. Replace("{x}", x.ToString()). Replace("{y}", y.ToString()). Replace("{z}", zoomLevel.ToString())); @@ -104,7 +104,7 @@ namespace MapControl { var hostIndex = (x + y) % 3; - return new Uri(UriFormat. + return new Uri(uriFormat. Replace("{c}", "abc".Substring(hostIndex, 1)). Replace("{x}", x.ToString()). Replace("{y}", y.ToString()). @@ -115,7 +115,7 @@ namespace MapControl { var hostIndex = (x + y) % 4; - return new Uri(UriFormat. + return new Uri(uriFormat. Replace("{i}", hostIndex.ToString()). Replace("{x}", x.ToString()). Replace("{y}", y.ToString()). @@ -126,7 +126,7 @@ namespace MapControl { var hostIndex = (x + y) % 4 + 1; - return new Uri(UriFormat. + return new Uri(uriFormat. Replace("{n}", hostIndex.ToString()). Replace("{x}", x.ToString()). Replace("{y}", y.ToString()). @@ -137,7 +137,7 @@ namespace MapControl { y = (1 << zoomLevel) - 1 - y; - return new Uri(UriFormat. + return new Uri(uriFormat. Replace("{x}", x.ToString()). Replace("{v}", y.ToString()). Replace("{z}", zoomLevel.ToString())); @@ -157,7 +157,7 @@ namespace MapControl key[z] = (char)('0' + 2 * (y % 2) + (x % 2)); } - return new Uri(UriFormat. + return new Uri(uriFormat. Replace("{i}", key.ToString(key.Length - 1, 1)). Replace("{q}", key.ToString())); } @@ -171,7 +171,7 @@ namespace MapControl var y1 = m * (180d - (double)(y + 1) * 360d / n); var y2 = m * (180d - (double)y * 360d / n); - return new Uri(UriFormat. + return new Uri(uriFormat. Replace("{W}", x1.ToString(CultureInfo.InvariantCulture)). Replace("{S}", y1.ToString(CultureInfo.InvariantCulture)). Replace("{E}", x2.ToString(CultureInfo.InvariantCulture)). @@ -189,7 +189,7 @@ namespace MapControl var p1 = t.Transform(new Point(x1, y1)); var p2 = t.Transform(new Point(x2, y2)); - return new Uri(UriFormat. + return new Uri(uriFormat. Replace("{w}", p1.Longitude.ToString(CultureInfo.InvariantCulture)). Replace("{s}", p1.Latitude.ToString(CultureInfo.InvariantCulture)). Replace("{e}", p2.Longitude.ToString(CultureInfo.InvariantCulture)). diff --git a/MapControl/TransformEx.WinRT.cs b/MapControl/TransformEx.WinRT.cs deleted file mode 100644 index 63e7ff49..00000000 --- a/MapControl/TransformEx.WinRT.cs +++ /dev/null @@ -1,17 +0,0 @@ -// XAML Map Control - http://xamlmapcontrol.codeplex.com/ -// Copyright © 2014 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); - } - } -} diff --git a/MapControl/WinRT/MapControl.WinRT.csproj b/MapControl/WinRT/MapControl.WinRT.csproj index af7c1cfd..fc95fecc 100644 --- a/MapControl/WinRT/MapControl.WinRT.csproj +++ b/MapControl/WinRT/MapControl.WinRT.csproj @@ -13,17 +13,17 @@ MapControl.WinRT en-US 512 - {BC8A1FFA-BEE3-4634-8014-F334798102B3};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 12.0 - Windows - 8.1 + Profile32 + v4.6 true full false ..\bin\Debug\ - TRACE;DEBUG;NETFX_CORE + TRACE;DEBUG;WINDOWS_RUNTIME prompt 4 @@ -31,16 +31,22 @@ none true ..\bin\Release\ - TRACE;NETFX_CORE + TRACE;WINDOWS_RUNTIME prompt 4 - - AnimationEx.WinRT.cs + + Extensions.Silverlight.WinRT.cs - - ImageTileSource.cs + + Extensions.WinRT.cs + + + ImageTileSource.Silverlight.WinRT.cs + + + IMapElement.cs Int32Rect.cs @@ -81,6 +87,12 @@ MapItemsControl.Silverlight.WinRT.cs + + MapOverlay.cs + + + MapOverlay.Silverlight.WinRT.cs + MapPanel.cs @@ -105,12 +117,12 @@ MapTransform.cs - - MatrixEx.cs - MercatorTransform.cs + + PanelBase.cs + Pushpin.Silverlight.WinRT.cs @@ -132,18 +144,12 @@ TileLayer.cs - - TileLayer.Silverlight.WinRT.cs - TileLayerCollection.cs TileSource.cs - - TransformEx.WinRT.cs - @@ -157,6 +163,10 @@ Designer + + + + 12.0 @@ -169,7 +179,7 @@ ..\..\MapControl.snk - + diff --git a/MapControl/WinRT/Properties/AssemblyInfo.cs b/MapControl/WinRT/Properties/AssemblyInfo.cs index e6822839..b2659bae 100644 --- a/MapControl/WinRT/Properties/AssemblyInfo.cs +++ b/MapControl/WinRT/Properties/AssemblyInfo.cs @@ -3,13 +3,12 @@ using System.Runtime.InteropServices; [assembly: AssemblyTitle("XAML Map Control (WinRT)")] [assembly: AssemblyDescription("XAML Map Control Library for Windows Runtime")] - [assembly: AssemblyProduct("XAML Map Control")] [assembly: AssemblyCompany("Clemens Fischer")] [assembly: AssemblyCopyright("Copyright © 2014 Clemens Fischer")] [assembly: AssemblyTrademark("")] -[assembly: AssemblyVersion("1.12.1")] -[assembly: AssemblyFileVersion("1.12.1")] +[assembly: AssemblyVersion("2.0.0")] +[assembly: AssemblyFileVersion("2.0.0")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCulture("")] [assembly: ComVisible(false)] diff --git a/SampleApps/Common/ViewModel.cs b/SampleApps/Common/ViewModel.cs index ea11efd5..0e5648ab 100644 --- a/SampleApps/Common/ViewModel.cs +++ b/SampleApps/Common/ViewModel.cs @@ -2,7 +2,7 @@ using System.Collections.ObjectModel; using System.ComponentModel; using System.Linq; -#if NETFX_CORE +#if WINDOWS_RUNTIME using Windows.UI.Xaml; #else using System.Windows.Threading; @@ -15,11 +15,12 @@ namespace ViewModel { public event PropertyChangedEventHandler PropertyChanged; - protected void OnPropertyChanged(string propertyName) + protected void RaisePropertyChanged(string propertyName) { - if (PropertyChanged != null) + var propertyChanged = PropertyChanged; + if (propertyChanged != null) { - PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); + propertyChanged(this, new PropertyChangedEventArgs(propertyName)); } } } @@ -33,7 +34,7 @@ namespace ViewModel set { name = value; - OnPropertyChanged("Name"); + RaisePropertyChanged("Name"); } } @@ -44,7 +45,7 @@ namespace ViewModel set { location = value; - OnPropertyChanged("Location"); + RaisePropertyChanged("Location"); } } } @@ -67,7 +68,7 @@ namespace ViewModel set { mapCenter = value; - OnPropertyChanged("MapCenter"); + RaisePropertyChanged("MapCenter"); } } diff --git a/SampleApps/PhoneApplication/App.xaml b/SampleApps/PhoneApplication/App.xaml new file mode 100644 index 00000000..fa95c1b9 --- /dev/null +++ b/SampleApps/PhoneApplication/App.xaml @@ -0,0 +1,7 @@ + + + \ No newline at end of file diff --git a/SampleApps/PhoneApplication/App.xaml.cs b/SampleApps/PhoneApplication/App.xaml.cs new file mode 100644 index 00000000..aec98412 --- /dev/null +++ b/SampleApps/PhoneApplication/App.xaml.cs @@ -0,0 +1,131 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices.WindowsRuntime; +using Windows.ApplicationModel; +using Windows.ApplicationModel.Activation; +using Windows.Foundation; +using Windows.Foundation.Collections; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Controls.Primitives; +using Windows.UI.Xaml.Data; +using Windows.UI.Xaml.Input; +using Windows.UI.Xaml.Media; +using Windows.UI.Xaml.Media.Animation; +using Windows.UI.Xaml.Navigation; + +// The Blank Application template is documented at http://go.microsoft.com/fwlink/?LinkId=391641 + +namespace PhoneApplication +{ + /// + /// Provides application-specific behavior to supplement the default Application class. + /// + public sealed partial class App : Application + { + private TransitionCollection transitions; + + /// + /// Initializes the singleton application object. This is the first line of authored code + /// executed, and as such is the logical equivalent of main() or WinMain(). + /// + public App() + { + this.InitializeComponent(); + this.Suspending += this.OnSuspending; + } + + /// + /// Invoked when the application is launched normally by the end user. Other entry points + /// will be used when the application is launched to open a specific file, to display + /// search results, and so forth. + /// + /// Details about the launch request and process. + protected override void OnLaunched(LaunchActivatedEventArgs e) + { +#if DEBUG + if (System.Diagnostics.Debugger.IsAttached) + { + this.DebugSettings.EnableFrameRateCounter = true; + } +#endif + + Frame rootFrame = Window.Current.Content as Frame; + + // Do not repeat app initialization when the Window already has content, + // just ensure that the window is active + if (rootFrame == null) + { + // Create a Frame to act as the navigation context and navigate to the first page + rootFrame = new Frame(); + + // TODO: change this value to a cache size that is appropriate for your application + rootFrame.CacheSize = 1; + + if (e.PreviousExecutionState == ApplicationExecutionState.Terminated) + { + // TODO: Load state from previously suspended application + } + + // Place the frame in the current Window + Window.Current.Content = rootFrame; + } + + if (rootFrame.Content == null) + { + // Removes the turnstile navigation for startup. + if (rootFrame.ContentTransitions != null) + { + this.transitions = new TransitionCollection(); + foreach (var c in rootFrame.ContentTransitions) + { + this.transitions.Add(c); + } + } + + rootFrame.ContentTransitions = null; + rootFrame.Navigated += this.RootFrame_FirstNavigated; + + // When the navigation stack isn't restored navigate to the first page, + // configuring the new page by passing required information as a navigation + // parameter + if (!rootFrame.Navigate(typeof(MainPage), e.Arguments)) + { + throw new Exception("Failed to create initial page"); + } + } + + // Ensure the current window is active + Window.Current.Activate(); + } + + /// + /// Restores the content transitions after the app has launched. + /// + /// The object where the handler is attached. + /// Details about the navigation event. + private void RootFrame_FirstNavigated(object sender, NavigationEventArgs e) + { + var rootFrame = sender as Frame; + rootFrame.ContentTransitions = this.transitions ?? new TransitionCollection() { new NavigationThemeTransition() }; + rootFrame.Navigated -= this.RootFrame_FirstNavigated; + } + + /// + /// Invoked when application execution is being suspended. Application state is saved + /// without knowing whether the application will be terminated or resumed with the contents + /// of memory still intact. + /// + /// The source of the suspend request. + /// Details about the suspend request. + private void OnSuspending(object sender, SuspendingEventArgs e) + { + var deferral = e.SuspendingOperation.GetDeferral(); + + // TODO: Save application state and stop any background activity + deferral.Complete(); + } + } +} \ No newline at end of file diff --git a/SampleApps/PhoneApplication/Assets/Logo.scale-240.png b/SampleApps/PhoneApplication/Assets/Logo.scale-240.png new file mode 100644 index 00000000..76921ca9 Binary files /dev/null and b/SampleApps/PhoneApplication/Assets/Logo.scale-240.png differ diff --git a/SampleApps/PhoneApplication/Assets/SmallLogo.scale-240.png b/SampleApps/PhoneApplication/Assets/SmallLogo.scale-240.png new file mode 100644 index 00000000..31663012 Binary files /dev/null and b/SampleApps/PhoneApplication/Assets/SmallLogo.scale-240.png differ diff --git a/SampleApps/PhoneApplication/Assets/SplashScreen.scale-240.png b/SampleApps/PhoneApplication/Assets/SplashScreen.scale-240.png new file mode 100644 index 00000000..33f26b33 Binary files /dev/null and b/SampleApps/PhoneApplication/Assets/SplashScreen.scale-240.png differ diff --git a/SampleApps/PhoneApplication/Assets/Square71x71Logo.scale-240.png b/SampleApps/PhoneApplication/Assets/Square71x71Logo.scale-240.png new file mode 100644 index 00000000..cfa54bee Binary files /dev/null and b/SampleApps/PhoneApplication/Assets/Square71x71Logo.scale-240.png differ diff --git a/SampleApps/PhoneApplication/Assets/StoreLogo.scale-240.png b/SampleApps/PhoneApplication/Assets/StoreLogo.scale-240.png new file mode 100644 index 00000000..47e084b5 Binary files /dev/null and b/SampleApps/PhoneApplication/Assets/StoreLogo.scale-240.png differ diff --git a/SampleApps/PhoneApplication/Assets/WideLogo.scale-240.png b/SampleApps/PhoneApplication/Assets/WideLogo.scale-240.png new file mode 100644 index 00000000..6249d29d Binary files /dev/null and b/SampleApps/PhoneApplication/Assets/WideLogo.scale-240.png differ diff --git a/SampleApps/PhoneApplication/MainPage.xaml b/SampleApps/PhoneApplication/MainPage.xaml new file mode 100644 index 00000000..e2c9b597 --- /dev/null +++ b/SampleApps/PhoneApplication/MainPage.xaml @@ -0,0 +1,116 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/SampleApps/PhoneApplication/MainPage.xaml.cs b/SampleApps/PhoneApplication/MainPage.xaml.cs new file mode 100644 index 00000000..7493bb85 --- /dev/null +++ b/SampleApps/PhoneApplication/MainPage.xaml.cs @@ -0,0 +1,87 @@ +using System; +using MapControl; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Data; +using Windows.UI.Xaml.Input; +using Windows.UI.Xaml.Navigation; + +namespace PhoneApplication +{ + public sealed partial class MainPage : Page + { + private bool manipulationActive; + + public MainPage() + { + InitializeComponent(); + DataContext = new ViewModel(Dispatcher); + NavigationCacheMode = NavigationCacheMode.Required; + } + + private void SeamarksChecked(object sender, RoutedEventArgs e) + { + var tileLayers = (TileLayerCollection)Resources["TileLayers"]; + map.TileLayers.Add((TileLayer)tileLayers["Seamarks"]); + } + + private void SeamarksUnchecked(object sender, RoutedEventArgs e) + { + var tileLayers = (TileLayerCollection)Resources["TileLayers"]; + map.TileLayers.Remove((TileLayer)tileLayers["Seamarks"]); + } + + private void MapMenuItemClick(object sender, RoutedEventArgs e) + { + var selectedValue = ((MenuFlyoutItem)sender).Text; + var tileLayers = (TileLayerCollection)Resources["TileLayers"]; + map.TileLayer = tileLayers[selectedValue]; + } + + private void CenterButtonClick(object sender, RoutedEventArgs e) + { + manipulationActive = false; + map.TargetCenter = ((ViewModel)DataContext).Location; + } + + private void MapManipulationStarted(object sender, ManipulationStartedRoutedEventArgs e) + { + manipulationActive = true; + } + + private void MapManipulationCompleted(object sender, ManipulationCompletedRoutedEventArgs e) + { + manipulationActive = false; + } + + private void MapManipulationDelta(object sender, ManipulationDeltaRoutedEventArgs e) + { + if (manipulationActive) + { + map.TransformMap(e.Position, e.Delta.Translation, e.Delta.Rotation, e.Delta.Scale); + } + else + { + e.Complete(); + } + } + } + + public class ObjectReferenceConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, string language) + { + if (targetType == typeof(Visibility)) + { + return value != null ? Visibility.Visible : Visibility.Collapsed; + } + + return value != null; + } + + public object ConvertBack(object value, Type targetType, object parameter, string language) + { + throw new NotSupportedException(); + } + } +} diff --git a/SampleApps/PhoneApplication/Package.appxmanifest b/SampleApps/PhoneApplication/Package.appxmanifest new file mode 100644 index 00000000..05637d6c --- /dev/null +++ b/SampleApps/PhoneApplication/Package.appxmanifest @@ -0,0 +1,30 @@ + + + + + + PhoneApplication + Clemens + Assets\StoreLogo.png + + + 6.3.1 + 6.3.1 + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/SampleApps/PhoneApplication/PhoneApplication.csproj b/SampleApps/PhoneApplication/PhoneApplication.csproj new file mode 100644 index 00000000..ec1fd6f6 --- /dev/null +++ b/SampleApps/PhoneApplication/PhoneApplication.csproj @@ -0,0 +1,135 @@ + + + + + Debug + AnyCPU + {8D0A57DF-FABF-4AEE-8768-9C18B2B43CA9} + AppContainerExe + Properties + PhoneApplication + PhoneApplication + en-US + 8.1 + 12 + 512 + {76F1466A-8B6D-4E39-A767-685A06062A39};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE;NETFX_CORE;WINDOWS_PHONE_APP + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE;NETFX_CORE;WINDOWS_PHONE_APP + prompt + 4 + + + true + bin\ARM\Debug\ + DEBUG;TRACE;NETFX_CORE;WINDOWS_PHONE_APP + ;2008 + full + ARM + false + prompt + true + + + bin\ARM\Release\ + TRACE;NETFX_CORE;WINDOWS_PHONE_APP + true + ;2008 + pdbonly + ARM + false + prompt + true + + + true + bin\x86\Debug\ + DEBUG;TRACE;NETFX_CORE;WINDOWS_PHONE_APP + ;2008 + full + x86 + false + prompt + true + + + bin\x86\Release\ + TRACE;NETFX_CORE;WINDOWS_PHONE_APP + true + ;2008 + pdbonly + x86 + false + prompt + true + + + + + {63cefdf7-5170-43b6-86f8-5c4a383a1615} + MapControl.WinRT + + + + + App.xaml + + + MainPage.xaml + + + + + + + Designer + + + + + + + + + + + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + + 12.0 + + + WindowsPhoneApp + + + + \ No newline at end of file diff --git a/SampleApps/PhoneApplication/PhoneApplication.csproj.user b/SampleApps/PhoneApplication/PhoneApplication.csproj.user new file mode 100644 index 00000000..790515dc --- /dev/null +++ b/SampleApps/PhoneApplication/PhoneApplication.csproj.user @@ -0,0 +1,9 @@ + + + + 30F105C9-681E-420b-A277-7C086EAD8A4E + + + 30F105C9-681E-420b-A277-7C086EAD8A4E + + \ No newline at end of file diff --git a/SampleApps/PhoneApplication/Properties/AssemblyInfo.cs b/SampleApps/PhoneApplication/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..59a482cd --- /dev/null +++ b/SampleApps/PhoneApplication/Properties/AssemblyInfo.cs @@ -0,0 +1,14 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +[assembly: AssemblyTitle("Windows Phone Sample Application")] +[assembly: AssemblyDescription("XAML Map Control Windows Phone Sample Application")] +[assembly: AssemblyProduct("XAML Map Control")] +[assembly: AssemblyCompany("Clemens Fischer")] +[assembly: AssemblyCopyright("Copyright © 2014 Clemens Fischer")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyVersion("2.0.0")] +[assembly: AssemblyFileVersion("2.0.0")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCulture("")] +[assembly: ComVisible(false)] \ No newline at end of file diff --git a/SampleApps/PhoneApplication/ViewModel.cs b/SampleApps/PhoneApplication/ViewModel.cs new file mode 100644 index 00000000..10857534 --- /dev/null +++ b/SampleApps/PhoneApplication/ViewModel.cs @@ -0,0 +1,84 @@ +using System; +using System.ComponentModel; +using MapControl; +using Windows.Devices.Geolocation; +using Windows.UI.Core; + +namespace PhoneApplication +{ + public class ViewModel : INotifyPropertyChanged + { + private readonly CoreDispatcher dispatcher; + private readonly Geolocator geoLocator; + private double accuracy; + private Location location; + + public event PropertyChangedEventHandler PropertyChanged; + + public ViewModel(CoreDispatcher dispatcher) + { + this.dispatcher = dispatcher; + + geoLocator = new Geolocator + { + DesiredAccuracy = PositionAccuracy.High, + MovementThreshold = 1d + }; + + geoLocator.StatusChanged += GeoLocatorStatusChanged; + geoLocator.PositionChanged += GeoLocatorPositionChanged; + } + + public double Accuracy + { + get { return accuracy; } + private set + { + accuracy = value; + RaisePropertyChanged("Accuracy"); + } + } + + public Location Location + { + get { return location; } + private set + { + location = value; + RaisePropertyChanged("Location"); + } + } + + private void RaisePropertyChanged(string propertyName) + { + var propertyChanged = PropertyChanged; + if (propertyChanged != null) + { + propertyChanged(this, new PropertyChangedEventArgs(propertyName)); + } + } + + private async void GeoLocatorStatusChanged(Geolocator sender, StatusChangedEventArgs args) + { + if (args.Status != PositionStatus.Initializing && + args.Status != PositionStatus.Ready) + { + await dispatcher.RunAsync(CoreDispatcherPriority.Low, () => + { + Location = null; + }); + } + } + + private async void GeoLocatorPositionChanged(Geolocator sender, PositionChangedEventArgs args) + { + await dispatcher.RunAsync(CoreDispatcherPriority.Low, () => + { + Accuracy = args.Position.Coordinate.Accuracy; + Location = new Location( + args.Position.Coordinate.Point.Position.Latitude, + args.Position.Coordinate.Point.Position.Longitude); + }); + } + } +} diff --git a/SampleApps/SilverlightApplication.Web/Properties/AssemblyInfo.cs b/SampleApps/SilverlightApplication.Web/Properties/AssemblyInfo.cs index 585f0146..fa4fde76 100644 --- a/SampleApps/SilverlightApplication.Web/Properties/AssemblyInfo.cs +++ b/SampleApps/SilverlightApplication.Web/Properties/AssemblyInfo.cs @@ -3,13 +3,12 @@ using System.Runtime.InteropServices; [assembly: AssemblyTitle("Silverlight/Web Sample Application")] [assembly: AssemblyDescription("XAML Map Control Silverlight/Web Sample Application")] - [assembly: AssemblyProduct("XAML Map Control")] [assembly: AssemblyCompany("Clemens Fischer")] [assembly: AssemblyCopyright("Copyright © 2014 Clemens Fischer")] [assembly: AssemblyTrademark("")] -[assembly: AssemblyVersion("1.12.1")] -[assembly: AssemblyFileVersion("1.12.1")] +[assembly: AssemblyVersion("2.0.0")] +[assembly: AssemblyFileVersion("2.0.0")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCulture("")] [assembly: ComVisible(false)] diff --git a/SampleApps/SilverlightApplication/Properties/AssemblyInfo.cs b/SampleApps/SilverlightApplication/Properties/AssemblyInfo.cs index 041a2e25..a6ef69a5 100644 --- a/SampleApps/SilverlightApplication/Properties/AssemblyInfo.cs +++ b/SampleApps/SilverlightApplication/Properties/AssemblyInfo.cs @@ -3,13 +3,12 @@ using System.Runtime.InteropServices; [assembly: AssemblyTitle("Silverlight Sample Application")] [assembly: AssemblyDescription("XAML Map Control Silverlight Sample Application")] - [assembly: AssemblyProduct("XAML Map Control")] [assembly: AssemblyCompany("Clemens Fischer")] [assembly: AssemblyCopyright("Copyright © 2014 Clemens Fischer")] [assembly: AssemblyTrademark("")] -[assembly: AssemblyVersion("1.12.1")] -[assembly: AssemblyFileVersion("1.12.1")] +[assembly: AssemblyVersion("2.0.0")] +[assembly: AssemblyFileVersion("2.0.0")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCulture("")] [assembly: ComVisible(false)] diff --git a/SampleApps/StoreApplication/MainPage.xaml b/SampleApps/StoreApplication/MainPage.xaml index aa5e5070..f6d83196 100644 --- a/SampleApps/StoreApplication/MainPage.xaml +++ b/SampleApps/StoreApplication/MainPage.xaml @@ -2,26 +2,29 @@ x:Class="StoreApplication.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" - xmlns:d="http://schemas.microsoft.com/expression/blend/2008" - xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:map="using:MapControl" xmlns:vm="using:ViewModel" - xmlns:local="using:StoreApplication" - mc:Ignorable="d"> + xmlns:local="using:StoreApplication"> - - - - - - + + + + + + + + + + + + + + + + + + @@ -180,7 +183,8 @@ - + OpenStreetMap OpenCycleMap OCM Transport diff --git a/SampleApps/StoreApplication/MainPage.xaml.cs b/SampleApps/StoreApplication/MainPage.xaml.cs index 0b736185..1cd81486 100644 --- a/SampleApps/StoreApplication/MainPage.xaml.cs +++ b/SampleApps/StoreApplication/MainPage.xaml.cs @@ -10,7 +10,6 @@ namespace StoreApplication public MainPage() { this.InitializeComponent(); - tileLayerComboBox.SelectedIndex = 0; } private void ImageOpacitySliderValueChanged(object sender, RangeBaseValueChangedEventArgs e) @@ -21,21 +20,28 @@ namespace StoreApplication } } + private void TileLayerComboBoxLoaded(object sender, RoutedEventArgs e) + { + ((ComboBox)sender).SelectedIndex = 0; + } + private void TileLayerSelectionChanged(object sender, SelectionChangedEventArgs e) { - var comboBox = (ComboBox)sender; + var selectedValue = (string)((ComboBox)sender).SelectedValue; var tileLayers = (TileLayerCollection)Resources["TileLayers"]; - map.TileLayer = tileLayers[(string)((ComboBoxItem)comboBox.SelectedItem).Content]; + map.TileLayer = tileLayers[selectedValue]; } private void SeamarksChecked(object sender, RoutedEventArgs e) { - map.TileLayers.Add((TileLayer)((TileLayerCollection)Resources["TileLayers"])["Seamarks"]); + var tileLayers = (TileLayerCollection)Resources["TileLayers"]; + map.TileLayers.Add((TileLayer)tileLayers["Seamarks"]); } private void SeamarksUnchecked(object sender, RoutedEventArgs e) { - map.TileLayers.Remove((TileLayer)((TileLayerCollection)Resources["TileLayers"])["Seamarks"]); + var tileLayers = (TileLayerCollection)Resources["TileLayers"]; + map.TileLayers.Remove((TileLayer)tileLayers["Seamarks"]); } } } diff --git a/SampleApps/StoreApplication/Properties/AssemblyInfo.cs b/SampleApps/StoreApplication/Properties/AssemblyInfo.cs index 2d4c6ae0..f9feb655 100644 --- a/SampleApps/StoreApplication/Properties/AssemblyInfo.cs +++ b/SampleApps/StoreApplication/Properties/AssemblyInfo.cs @@ -3,13 +3,12 @@ using System.Runtime.InteropServices; [assembly: AssemblyTitle("Windows Runtime Sample Application")] [assembly: AssemblyDescription("XAML Map Control Windows Runtime Sample Application")] - [assembly: AssemblyProduct("XAML Map Control")] [assembly: AssemblyCompany("Clemens Fischer")] [assembly: AssemblyCopyright("Copyright © 2014 Clemens Fischer")] [assembly: AssemblyTrademark("")] -[assembly: AssemblyVersion("1.12.1")] -[assembly: AssemblyFileVersion("1.12.1")] +[assembly: AssemblyVersion("2.0.0")] +[assembly: AssemblyFileVersion("2.0.0")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCulture("")] [assembly: ComVisible(false)] diff --git a/SampleApps/StoreApplication/StoreApplication.csproj b/SampleApps/StoreApplication/StoreApplication.csproj index 50fce977..ffada2ff 100644 --- a/SampleApps/StoreApplication/StoreApplication.csproj +++ b/SampleApps/StoreApplication/StoreApplication.csproj @@ -23,7 +23,7 @@ full false bin\Debug\ - DEBUG;TRACE;NETFX_CORE + TRACE;DEBUG;WINDOWS_RUNTIME prompt 4 @@ -32,7 +32,7 @@ pdbonly true bin\Release\ - TRACE;NETFX_CORE + TRACE;WINDOWS_RUNTIME prompt 4 diff --git a/SampleApps/SurfaceApplication/Properties/AssemblyInfo.cs b/SampleApps/SurfaceApplication/Properties/AssemblyInfo.cs index ab01a4d7..be1f7b7c 100644 --- a/SampleApps/SurfaceApplication/Properties/AssemblyInfo.cs +++ b/SampleApps/SurfaceApplication/Properties/AssemblyInfo.cs @@ -8,8 +8,8 @@ using System.Runtime.InteropServices; [assembly: AssemblyCompany("Clemens Fischer")] [assembly: AssemblyCopyright("Copyright © 2014 Clemens Fischer")] [assembly: AssemblyTrademark("")] -[assembly: AssemblyVersion("1.12.1")] -[assembly: AssemblyFileVersion("1.12.1")] +[assembly: AssemblyVersion("2.0.0")] +[assembly: AssemblyFileVersion("2.0.0")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCulture("")] [assembly: ComVisible(false)] diff --git a/SampleApps/WpfApplication/MainWindow.xaml b/SampleApps/WpfApplication/MainWindow.xaml index bb056196..98a9188d 100644 --- a/SampleApps/WpfApplication/MainWindow.xaml +++ b/SampleApps/WpfApplication/MainWindow.xaml @@ -41,7 +41,7 @@ diff --git a/SampleApps/WpfApplication/Properties/AssemblyInfo.cs b/SampleApps/WpfApplication/Properties/AssemblyInfo.cs index db7ebace..7fc78994 100644 --- a/SampleApps/WpfApplication/Properties/AssemblyInfo.cs +++ b/SampleApps/WpfApplication/Properties/AssemblyInfo.cs @@ -3,13 +3,12 @@ using System.Runtime.InteropServices; [assembly: AssemblyTitle("WPF Sample Application")] [assembly: AssemblyDescription("XAML Map Control WPF Sample Application")] - [assembly: AssemblyProduct("XAML Map Control")] [assembly: AssemblyCompany("Clemens Fischer")] [assembly: AssemblyCopyright("Copyright © 2014 Clemens Fischer")] [assembly: AssemblyTrademark("")] -[assembly: AssemblyVersion("1.12.1")] -[assembly: AssemblyFileVersion("1.12.1")] +[assembly: AssemblyVersion("2.0.0")] +[assembly: AssemblyFileVersion("2.0.0")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCulture("")] [assembly: ComVisible(false)]