Version 7.0: WinUI

This commit is contained in:
Clemens 2021-11-17 23:17:11 +01:00
parent 448f599d96
commit 9f61b90795
56 changed files with 94 additions and 81 deletions

View file

@ -1,55 +0,0 @@
// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2021 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL)
#if WINUI
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Media.Animation;
#else
using Windows.UI.Xaml;
using Windows.UI.Xaml.Media.Animation;
#endif
namespace MapControl
{
internal static class Animatable
{
public static void BeginAnimation(this DependencyObject obj, DependencyProperty property, Timeline animation)
{
if (animation != null)
{
string propertyName = null;
if (property == MapBase.CenterPointProperty)
{
propertyName = "CenterPoint";
((PointAnimation)animation).EnableDependentAnimation = true;
}
else if (property == MapBase.ZoomLevelProperty)
{
propertyName = "ZoomLevel";
((DoubleAnimation)animation).EnableDependentAnimation = true;
}
else if (property == MapBase.HeadingProperty)
{
propertyName = "Heading";
((DoubleAnimation)animation).EnableDependentAnimation = true;
}
else if (property == UIElement.OpacityProperty)
{
propertyName = "Opacity";
}
if (propertyName != null)
{
Storyboard.SetTargetProperty(animation, propertyName);
Storyboard.SetTarget(animation, obj);
var storyboard = new Storyboard();
storyboard.Children.Add(animation);
storyboard.Begin();
}
}
}
}
}

View file

@ -1,60 +0,0 @@
// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2021 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL)
using System;
using System.Diagnostics;
using System.IO;
using System.Threading.Tasks;
namespace MapControl.Caching
{
public partial class ImageFileCache : IImageCache
{
public async Task<Tuple<byte[], DateTime>> GetAsync(string key)
{
Tuple<byte[], DateTime> cacheItem = null;
var path = GetPath(key);
try
{
if (path != null && File.Exists(path))
{
var buffer = await File.ReadAllBytesAsync(path);
var expiration = ReadExpiration(ref buffer);
cacheItem = Tuple.Create(buffer, expiration);
}
}
catch (Exception ex)
{
Debug.WriteLine($"ImageFileCache: Failed reading {path}: {ex.Message}");
}
return cacheItem;
}
public async Task SetAsync(string key, byte[] buffer, DateTime expiration)
{
var path = GetPath(key);
if (buffer != null && buffer.Length > 0 && path != null)
{
try
{
Directory.CreateDirectory(Path.GetDirectoryName(path));
using (var stream = File.Create(path))
{
await stream.WriteAsync(buffer, 0, buffer.Length);
await WriteExpirationAsync(stream, expiration);
}
}
catch (Exception ex)
{
Debug.WriteLine($"ImageFileCache: Failed writing {path}: {ex.Message}");
}
}
}
}
}

View file

@ -1,61 +0,0 @@
// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2021 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL)
using System;
using System.IO;
using System.Threading.Tasks;
using Windows.Storage;
using Windows.Storage.Streams;
#if WINUI
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Media.Imaging;
#else
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Media.Imaging;
#endif
namespace MapControl
{
public static partial class ImageLoader
{
public static async Task<ImageSource> LoadImageAsync(IRandomAccessStream stream)
{
var image = new BitmapImage();
await image.SetSourceAsync(stream);
return image;
}
public static Task<ImageSource> LoadImageAsync(Stream stream)
{
return LoadImageAsync(stream.AsRandomAccessStream());
}
public static Task<ImageSource> LoadImageAsync(byte[] buffer)
{
using (var stream = new MemoryStream(buffer))
{
return LoadImageAsync(stream);
}
}
public static async Task<ImageSource> LoadImageAsync(string path)
{
ImageSource image = null;
if (File.Exists(path))
{
var file = await StorageFile.GetFileFromPathAsync(Path.GetFullPath(path));
using (var stream = await file.OpenReadAsync())
{
image = await LoadImageAsync(stream);
}
}
return image;
}
}
}

View file

@ -1,58 +0,0 @@
// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2021 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL)
using System;
#if WINUI
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Input;
#else
using Windows.UI.Xaml;
using Windows.UI.Xaml.Input;
#endif
namespace MapControl
{
/// <summary>
/// MapBase with default input event handling.
/// </summary>
public class Map : MapBase
{
public static readonly DependencyProperty MouseWheelZoomDeltaProperty = DependencyProperty.Register(
nameof(MouseWheelZoomDelta), typeof(double), typeof(Map), new PropertyMetadata(1d));
public Map()
{
ManipulationMode = ManipulationModes.Scale
| ManipulationModes.TranslateX
| ManipulationModes.TranslateY
| ManipulationModes.TranslateInertia;
ManipulationDelta += OnManipulationDelta;
PointerWheelChanged += OnPointerWheelChanged;
}
/// <summary>
/// Gets or sets the amount by which the ZoomLevel property changes during a MouseWheel event.
/// The default value is 1.
/// </summary>
public double MouseWheelZoomDelta
{
get { return (double)GetValue(MouseWheelZoomDeltaProperty); }
set { SetValue(MouseWheelZoomDeltaProperty, value); }
}
private void OnManipulationDelta(object sender, ManipulationDeltaRoutedEventArgs e)
{
TransformMap(e.Position, e.Delta.Translation, e.Delta.Rotation, e.Delta.Scale);
}
private void OnPointerWheelChanged(object sender, PointerRoutedEventArgs e)
{
var point = e.GetCurrentPoint(this);
var zoomLevel = TargetZoomLevel + MouseWheelZoomDelta * Math.Sign(point.Properties.MouseWheelDelta);
ZoomMap(point.Position, MouseWheelZoomDelta * Math.Round(zoomLevel / MouseWheelZoomDelta));
}
}
}

View file

@ -1,83 +0,0 @@
// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2021 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL)
#if WINUI
using Microsoft.UI;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Media;
#else
using Windows.UI;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Media;
#endif
namespace MapControl
{
public partial class MapBase
{
public static readonly DependencyProperty ForegroundProperty = DependencyProperty.Register(
nameof(Foreground), typeof(Brush), typeof(MapBase), new PropertyMetadata(new SolidColorBrush(Colors.Black)));
public static readonly DependencyProperty CenterProperty = DependencyProperty.Register(
nameof(Center), typeof(Location), typeof(MapBase),
new PropertyMetadata(new Location(), (o, e) => ((MapBase)o).CenterPropertyChanged((Location)e.NewValue)));
public static readonly DependencyProperty TargetCenterProperty = DependencyProperty.Register(
nameof(TargetCenter), typeof(Location), typeof(MapBase),
new PropertyMetadata(new Location(), (o, e) => ((MapBase)o).TargetCenterPropertyChanged((Location)e.NewValue)));
public static readonly DependencyProperty ZoomLevelProperty = DependencyProperty.Register(
nameof(ZoomLevel), typeof(double), typeof(MapBase),
new PropertyMetadata(1d, (o, e) => ((MapBase)o).ZoomLevelPropertyChanged((double)e.NewValue)));
public static readonly DependencyProperty TargetZoomLevelProperty = DependencyProperty.Register(
nameof(TargetZoomLevel), typeof(double), typeof(MapBase),
new PropertyMetadata(1d, (o, e) => ((MapBase)o).TargetZoomLevelPropertyChanged((double)e.NewValue)));
public static readonly DependencyProperty HeadingProperty = DependencyProperty.Register(
nameof(Heading), typeof(double), typeof(MapBase),
new PropertyMetadata(0d, (o, e) => ((MapBase)o).HeadingPropertyChanged((double)e.NewValue)));
public static readonly DependencyProperty TargetHeadingProperty = DependencyProperty.Register(
nameof(TargetHeading), typeof(double), typeof(MapBase),
new PropertyMetadata(0d, (o, e) => ((MapBase)o).TargetHeadingPropertyChanged((double)e.NewValue)));
public static readonly DependencyProperty ViewScaleProperty = DependencyProperty.Register(
nameof(ViewScale), typeof(double), typeof(MapBase), new PropertyMetadata(0d));
internal static readonly DependencyProperty CenterPointProperty = DependencyProperty.Register(
"CenterPoint", typeof(Windows.Foundation.Point), typeof(MapBase),
new PropertyMetadata(new Windows.Foundation.Point(), (o, e) =>
{
var center = (Windows.Foundation.Point)e.NewValue;
((MapBase)o).CenterPointPropertyChanged(new Location(center.Y, center.X));
}));
public MapBase()
{
// set Background by Style to enable resetting by ClearValue in MapLayerPropertyChanged
var style = new Style(typeof(MapBase));
style.Setters.Add(new Setter(BackgroundProperty, new SolidColorBrush(Colors.White)));
Style = style;
SizeChanged += OnSizeChanged;
}
private void OnSizeChanged(object sender, SizeChangedEventArgs e)
{
Clip = new RectangleGeometry
{
Rect = new Windows.Foundation.Rect(0d, 0d, e.NewSize.Width, e.NewSize.Height)
};
ResetTransformCenter();
UpdateTransform();
}
private void SetViewScale(double scale)
{
SetValue(ViewScaleProperty, scale);
}
}
}

View file

@ -1,77 +0,0 @@
// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2021 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL)
#if WINUI
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
#else
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
#endif
namespace MapControl
{
/// <summary>
/// ContentControl placed on a MapPanel at a geographic location specified by the Location property.
/// </summary>
public class MapContentControl : ContentControl
{
public static readonly DependencyProperty AutoCollapseProperty = DependencyProperty.Register(
nameof(AutoCollapse), typeof(bool), typeof(MapContentControl),
new PropertyMetadata(false, (o, e) => MapPanel.SetAutoCollapse((MapContentControl)o, (bool)e.NewValue)));
public static readonly DependencyProperty LocationProperty = DependencyProperty.Register(
nameof(Location), typeof(Location), typeof(MapContentControl),
new PropertyMetadata(null, (o, e) => MapPanel.SetLocation((MapContentControl)o, (Location)e.NewValue)));
public MapContentControl()
{
DefaultStyleKey = typeof(MapContentControl);
MapPanel.InitMapElement(this);
}
/// <summary>
/// Gets/sets MapPanel.AutoCollapse.
/// </summary>
public bool AutoCollapse
{
get { return (bool)GetValue(AutoCollapseProperty); }
set { SetValue(AutoCollapseProperty, value); }
}
/// <summary>
/// Gets/sets MapPanel.Location.
/// </summary>
public Location Location
{
get { return (Location)GetValue(LocationProperty); }
set { SetValue(LocationProperty, value); }
}
protected override void OnApplyTemplate()
{
base.OnApplyTemplate();
var parentMap = MapPanel.GetParentMap(this);
if (parentMap != null)
{
this.ValidateProperty(BackgroundProperty, parentMap, nameof(MapBase.Background));
this.ValidateProperty(BorderBrushProperty, parentMap, nameof(MapBase.Foreground));
this.ValidateProperty(ForegroundProperty, parentMap, nameof(MapBase.Foreground));
}
}
}
/// <summary>
/// MapContentControl with a Pushpin Style.
/// </summary>
public class Pushpin : MapContentControl
{
public Pushpin()
{
DefaultStyleKey = typeof(Pushpin);
}
}
}

View file

@ -23,7 +23,7 @@
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;WINDOWS_UWP</DefineConstants>
<DefineConstants>DEBUG;UWP</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
@ -32,7 +32,7 @@
<DebugType>none</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>WINDOWS_UWP</DefineConstants>
<DefineConstants>UWP</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<GenerateLibraryLayout>true</GenerateLibraryLayout>
@ -182,23 +182,53 @@
<Compile Include="..\Shared\WorldMercatorProjection.cs">
<Link>WorldMercatorProjection.cs</Link>
</Compile>
<Compile Include="Animatable.UWP.cs" />
<Compile Include="ImageFileCache.UWP.cs" />
<Compile Include="Map.UWP.cs" />
<Compile Include="MapBase.UWP.cs" />
<Compile Include="MapGraticule.UWP.cs" />
<Compile Include="MapItemsControl.UWP.cs" />
<Compile Include="MapOverlay.UWP.cs" />
<Compile Include="MapPanel.UWP.cs" />
<Compile Include="MapPath.UWP.cs" />
<Compile Include="Matrix.UWP.cs" />
<Compile Include="Point.UWP.cs" />
<Compile Include="..\WinUI\Animatable.WinUI.cs">
<Link>Animatable.WinUI.cs</Link>
</Compile>
<Compile Include="..\WinUI\ImageFileCache.WinUI.cs">
<Link>ImageFileCache.WinUI.cs</Link>
</Compile>
<Compile Include="..\WinUI\ImageLoader.WinUI.cs">
<Link>ImageLoader.WinUI.cs</Link>
</Compile>
<Compile Include="..\WinUI\Map.WinUI.cs">
<Link>Map.WinUI.cs</Link>
</Compile>
<Compile Include="..\WinUI\MapBase.WinUI.cs">
<Link>MapBase.WinUI.cs</Link>
</Compile>
<Compile Include="..\WinUI\MapContentControl.WinUI.cs">
<Link>MapContentControl.WinUI.cs</Link>
</Compile>
<Compile Include="..\WinUI\MapGraticule.WinUI.cs">
<Link>MapGraticule.WinUI.cs</Link>
</Compile>
<Compile Include="..\WinUI\MapItemsControl.WinUI.cs">
<Link>MapItemsControl.WinUI.cs</Link>
</Compile>
<Compile Include="..\WinUI\MapOverlay.WinUI.cs">
<Link>MapOverlay.WinUI.cs</Link>
</Compile>
<Compile Include="..\WinUI\MapPanel.WinUI.cs">
<Link>MapPanel.WinUI.cs</Link>
</Compile>
<Compile Include="..\WinUI\MapPath.WinUI.cs">
<Link>MapPath.WinUI.cs</Link>
</Compile>
<Compile Include="..\WinUI\Matrix.WinUI.cs">
<Link>Matrix.WinUI.cs</Link>
</Compile>
<Compile Include="..\WinUI\Point.WinUI.cs">
<Link>Point.WinUI.cs</Link>
</Compile>
<Compile Include="..\WinUI\Tile.WinUI.cs">
<Link>Tile.WinUI.cs</Link>
</Compile>
<Compile Include="..\WinUI\Vector.WinUI.cs">
<Link>Vector.WinUI.cs</Link>
</Compile>
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="MapContentControl.UWP.cs" />
<Compile Include="Tile.UWP.cs" />
<Compile Include="TileImageLoader.UWP.cs" />
<Compile Include="ImageLoader.UWP.cs" />
<Compile Include="Vector.UWP.cs" />
<EmbeddedResource Include="Properties\MapControl.UWP.rd.xml" />
</ItemGroup>
<ItemGroup>

View file

@ -1,170 +0,0 @@
// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2021 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL)
using System;
using Windows.Foundation;
#if WINUI
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Shapes;
#else
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Shapes;
#endif
namespace MapControl
{
public partial class MapGraticule
{
private Path path;
public MapGraticule()
{
StrokeThickness = 0.5;
}
protected override void OnViewportChanged(ViewportChangedEventArgs e)
{
var map = ParentMap;
var projection = map.MapProjection;
if (projection.IsNormalCylindrical)
{
if (path == null)
{
path = new Path { Data = new PathGeometry() };
path.SetBinding(Shape.StrokeProperty, this.GetBinding(nameof(Stroke)));
path.SetBinding(Shape.StrokeThicknessProperty, this.GetBinding(nameof(StrokeThickness)));
path.SetBinding(Shape.StrokeDashArrayProperty, this.GetBinding(nameof(StrokeDashArray)));
path.SetBinding(Shape.StrokeDashOffsetProperty, this.GetBinding(nameof(StrokeDashOffset)));
path.SetBinding(Shape.StrokeDashCapProperty, this.GetBinding(nameof(StrokeDashCap)));
Children.Add(path);
}
var bounds = map.ViewRectToBoundingBox(new Rect(0d, 0d, map.RenderSize.Width, map.RenderSize.Height));
var lineDistance = GetLineDistance();
var labelStart = new Location(
Math.Ceiling(bounds.South / lineDistance) * lineDistance,
Math.Ceiling(bounds.West / lineDistance) * lineDistance);
var labelEnd = new Location(
Math.Floor(bounds.North / lineDistance) * lineDistance,
Math.Floor(bounds.East / lineDistance) * lineDistance);
var lineStart = new Location(
Math.Min(Math.Max(labelStart.Latitude - lineDistance, -projection.MaxLatitude), projection.MaxLatitude),
labelStart.Longitude - lineDistance);
var lineEnd = new Location(
Math.Min(Math.Max(labelEnd.Latitude + lineDistance, -projection.MaxLatitude), projection.MaxLatitude),
labelEnd.Longitude + lineDistance);
var geometry = (PathGeometry)path.Data;
geometry.Figures.Clear();
for (var lat = labelStart.Latitude; lat <= bounds.North; lat += lineDistance)
{
var figure = new PathFigure
{
StartPoint = map.LocationToView(new Location(lat, lineStart.Longitude)),
IsClosed = false,
IsFilled = false
};
figure.Segments.Add(new LineSegment
{
Point = map.LocationToView(new Location(lat, lineEnd.Longitude))
});
geometry.Figures.Add(figure);
}
for (var lon = labelStart.Longitude; lon <= bounds.East; lon += lineDistance)
{
var figure = new PathFigure
{
StartPoint = map.LocationToView(new Location(lineStart.Latitude, lon)),
IsClosed = false,
IsFilled = false
};
figure.Segments.Add(new LineSegment
{
Point = map.LocationToView(new Location(lineEnd.Latitude, lon))
});
geometry.Figures.Add(figure);
}
var labelFormat = GetLabelFormat(lineDistance);
var childIndex = 1; // 0 for Path
for (var lat = labelStart.Latitude; lat <= bounds.North; lat += lineDistance)
{
for (var lon = labelStart.Longitude; lon <= bounds.East; lon += lineDistance)
{
TextBlock label;
if (childIndex < Children.Count)
{
label = (TextBlock)Children[childIndex];
}
else
{
label = new TextBlock { RenderTransform = new MatrixTransform() };
label.SetBinding(TextBlock.FontSizeProperty, this.GetBinding(nameof(FontSize)));
label.SetBinding(TextBlock.FontStyleProperty, this.GetBinding(nameof(FontStyle)));
label.SetBinding(TextBlock.FontStretchProperty, this.GetBinding(nameof(FontStretch)));
label.SetBinding(TextBlock.FontWeightProperty, this.GetBinding(nameof(FontWeight)));
label.SetBinding(TextBlock.ForegroundProperty, this.GetBinding(nameof(Foreground)));
if (FontFamily != null)
{
label.SetBinding(TextBlock.FontFamilyProperty, this.GetBinding(nameof(FontFamily)));
}
Children.Add(label);
}
childIndex++;
label.Text = GetLabelText(lat, labelFormat, "NS") + "\n" + GetLabelText(Location.NormalizeLongitude(lon), labelFormat, "EW");
label.Tag = new Location(lat, lon);
label.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
}
while (Children.Count > childIndex)
{
Children.RemoveAt(Children.Count - 1);
}
}
// don't use MapPanel.Location because labels may be at more than 180° distance from map center
for (int i = 1; i < Children.Count; i++)
{
var label = (TextBlock)Children[i];
var location = (Location)label.Tag;
var viewPosition = map.LocationToView(location);
var matrix = new Matrix(1, 0, 0, 1, 0, 0);
matrix.Translate(StrokeThickness / 2d + 2d, -label.DesiredSize.Height / 2d);
matrix.Rotate(map.ViewTransform.Rotation);
matrix.Translate(viewPosition.X, viewPosition.Y);
((MatrixTransform)label.RenderTransform).Matrix = matrix;
}
}
else if (path != null)
{
path = null;
Children.Clear();
}
base.OnViewportChanged(e);
}
}
}

View file

@ -1,68 +0,0 @@
// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2021 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL)
using Windows.System;
#if WINUI
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Input;
#else
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Input;
#endif
namespace MapControl
{
public partial class MapItem
{
public static readonly DependencyProperty AutoCollapseProperty = DependencyProperty.Register(
nameof(AutoCollapse), typeof(bool), typeof(MapItem),
new PropertyMetadata(false, (o, e) => MapPanel.SetAutoCollapse((MapItem)o, (bool)e.NewValue)));
public static readonly DependencyProperty LocationProperty = DependencyProperty.Register(
nameof(Location), typeof(Location), typeof(MapItem),
new PropertyMetadata(null, (o, e) => MapPanel.SetLocation((MapItem)o, (Location)e.NewValue)));
public MapItem()
{
DefaultStyleKey = typeof(MapItem);
MapPanel.InitMapElement(this);
}
protected override void OnPointerPressed(PointerRoutedEventArgs e)
{
(ItemsControl.ItemsControlFromItemContainer(this) as MapItemsControl)?.OnItemClicked(
this, e.KeyModifiers.HasFlag(VirtualKeyModifiers.Control), e.KeyModifiers.HasFlag(VirtualKeyModifiers.Shift));
}
protected override void OnApplyTemplate()
{
base.OnApplyTemplate();
var parentMap = MapPanel.GetParentMap(this);
if (parentMap != null)
{
this.ValidateProperty(BackgroundProperty, parentMap, nameof(MapBase.Background));
this.ValidateProperty(BorderBrushProperty, parentMap, nameof(MapBase.Foreground));
this.ValidateProperty(ForegroundProperty, parentMap, nameof(MapBase.Foreground));
}
}
}
public partial class MapItemsControl
{
public MapItemsControl()
{
DefaultStyleKey = typeof(MapItemsControl);
MapPanel.InitMapElement(this);
}
public new FrameworkElement ContainerFromItem(object item)
{
return (FrameworkElement)base.ContainerFromItem(item);
}
}
}

View file

@ -1,81 +0,0 @@
// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2021 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL)
#if WINUI
using Microsoft.UI.Text;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Media;
using Windows.UI.Text;
#else
using Windows.UI.Text;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Media;
#endif
namespace MapControl
{
public partial class MapOverlay
{
public static readonly DependencyProperty FontFamilyProperty = DependencyProperty.Register(
nameof(FontFamily), typeof(FontFamily), typeof(MapOverlay), new PropertyMetadata(null));
public static readonly DependencyProperty FontSizeProperty = DependencyProperty.Register(
nameof(FontSize), typeof(double), typeof(MapOverlay), new PropertyMetadata(12d));
public static readonly DependencyProperty FontStyleProperty = DependencyProperty.Register(
nameof(FontStyle), typeof(FontStyle), typeof(MapOverlay), new PropertyMetadata(FontStyle.Normal));
public static readonly DependencyProperty FontStretchProperty = DependencyProperty.Register(
nameof(FontStretch), typeof(FontStretch), typeof(MapOverlay), new PropertyMetadata(FontStretch.Normal));
public static readonly DependencyProperty FontWeightProperty = DependencyProperty.Register(
nameof(FontWeight), typeof(FontWeight), typeof(MapOverlay), new PropertyMetadata(FontWeights.Normal));
public static readonly DependencyProperty ForegroundProperty = DependencyProperty.Register(
nameof(Foreground), typeof(Brush), typeof(MapOverlay), new PropertyMetadata(null));
public static readonly DependencyProperty StrokeProperty = DependencyProperty.Register(
nameof(Stroke), typeof(Brush), typeof(MapOverlay), new PropertyMetadata(null));
public static readonly DependencyProperty StrokeThicknessProperty = DependencyProperty.Register(
nameof(StrokeThickness), typeof(double), typeof(MapOverlay), new PropertyMetadata(1d));
public static readonly DependencyProperty StrokeDashArrayProperty = DependencyProperty.Register(
nameof(StrokeDashArray), typeof(DoubleCollection), typeof(MapOverlay), new PropertyMetadata(null));
public static readonly DependencyProperty StrokeDashOffsetProperty = DependencyProperty.Register(
nameof(StrokeDashOffset), typeof(double), typeof(MapOverlay), new PropertyMetadata(0d));
public static readonly DependencyProperty StrokeDashCapProperty = DependencyProperty.Register(
nameof(StrokeDashCap), typeof(PenLineCap), typeof(MapOverlay), new PropertyMetadata(PenLineCap.Flat));
public static readonly DependencyProperty StrokeStartLineCapProperty = DependencyProperty.Register(
nameof(StrokeStartLineCap), typeof(PenLineCap), typeof(MapOverlay), new PropertyMetadata(PenLineCap.Flat));
public static readonly DependencyProperty StrokeEndLineCapProperty = DependencyProperty.Register(
nameof(StrokeEndLineCap), typeof(PenLineCap), typeof(MapOverlay), new PropertyMetadata(PenLineCap.Flat));
public static readonly DependencyProperty StrokeLineJoinProperty = DependencyProperty.Register(
nameof(StrokeLineJoin), typeof(PenLineJoin), typeof(MapOverlay), new PropertyMetadata(PenLineJoin.Miter));
public static readonly DependencyProperty StrokeMiterLimitProperty = DependencyProperty.Register(
nameof(StrokeMiterLimit), typeof(double), typeof(MapOverlay), new PropertyMetadata(1d));
public MapOverlay()
{
IsHitTestVisible = false;
}
protected override void SetParentMap(MapBase map)
{
if (map != null)
{
this.ValidateProperty(ForegroundProperty, map, nameof(MapBase.Foreground));
this.ValidateProperty(StrokeProperty, this, nameof(Foreground));
}
base.SetParentMap(map);
}
}
}

View file

@ -1,76 +0,0 @@
// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2021 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL)
#if WINUI
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Media;
#else
using Windows.UI.Xaml;
using Windows.UI.Xaml.Media;
#endif
namespace MapControl
{
public partial class MapPanel
{
public static readonly DependencyProperty LocationProperty = DependencyProperty.RegisterAttached(
"Location", typeof(Location), typeof(MapPanel),
new PropertyMetadata(null, (o, e) => (((FrameworkElement)o).Parent as MapPanel)?.InvalidateArrange()));
public static readonly DependencyProperty BoundingBoxProperty = DependencyProperty.RegisterAttached(
"BoundingBox", typeof(BoundingBox), typeof(MapPanel),
new PropertyMetadata(null, (o, e) => (((FrameworkElement)o).Parent as MapPanel)?.InvalidateArrange()));
public static readonly DependencyProperty ParentMapProperty = DependencyProperty.RegisterAttached(
"ParentMap", typeof(MapBase), typeof(MapPanel), new PropertyMetadata(null, ParentMapPropertyChanged));
private static readonly DependencyProperty ViewPositionProperty = DependencyProperty.RegisterAttached(
"ViewPosition", typeof(Point?), typeof(MapPanel), new PropertyMetadata(null));
public MapPanel()
{
InitMapElement(this);
}
public static void InitMapElement(FrameworkElement element)
{
if (element is MapBase)
{
element.SetValue(ParentMapProperty, element);
}
else
{
// Workaround for missing property value inheritance.
// Loaded and Unloaded handlers set and clear the ParentMap property value.
element.Loaded += (s, e) => GetParentMap(element);
element.Unloaded += (s, e) => element.ClearValue(ParentMapProperty);
}
}
public static MapBase GetParentMap(FrameworkElement element)
{
var parentMap = (MapBase)element.GetValue(ParentMapProperty);
if (parentMap == null && (parentMap = FindParentMap(element)) != null)
{
element.SetValue(ParentMapProperty, parentMap);
}
return parentMap;
}
private static MapBase FindParentMap(FrameworkElement element)
{
return VisualTreeHelper.GetParent(element) is FrameworkElement parent
? ((parent as MapBase) ?? (MapBase)element.GetValue(ParentMapProperty) ?? FindParentMap(parent))
: null;
}
private static void SetViewPosition(FrameworkElement element, Point? viewPosition)
{
element.SetValue(ViewPositionProperty, viewPosition);
}
}
}

View file

@ -1,124 +0,0 @@
// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2021 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL)
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using Windows.Foundation;
#if WINUI
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Shapes;
#else
using Windows.UI.Xaml;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Shapes;
#endif
namespace MapControl
{
public partial class MapPath : Path
{
public MapPath()
{
MapPanel.InitMapElement(this);
}
#region Methods used only by derived classes MapPolyline and MapPolygon
protected void DataCollectionPropertyChanged(DependencyPropertyChangedEventArgs e)
{
if (e.OldValue is INotifyCollectionChanged oldCollection)
{
oldCollection.CollectionChanged -= DataCollectionChanged;
}
if (e.NewValue is INotifyCollectionChanged newCollection)
{
newCollection.CollectionChanged += DataCollectionChanged;
}
UpdateData();
}
protected void DataCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
UpdateData();
}
protected void AddPolylineLocations(PathFigureCollection pathFigures, IEnumerable<Location> locations, double longitudeOffset, bool closed)
{
if (locations.Count() >= 2)
{
var points = locations.Select(location => LocationToView(location, longitudeOffset));
if (closed)
{
var segment = new PolyLineSegment();
foreach (var point in points.Skip(1))
{
segment.Points.Add(point);
}
var figure = new PathFigure
{
StartPoint = points.First(),
IsClosed = closed,
IsFilled = closed
};
figure.Segments.Add(segment);
pathFigures.Add(figure);
}
else
{
var pointList = points.ToList();
if (closed)
{
pointList.Add(pointList[0]);
}
var viewport = new Rect(0, 0, ParentMap.RenderSize.Width, ParentMap.RenderSize.Height);
PathFigure figure = null;
PolyLineSegment segment = null;
for (int i = 1; i < pointList.Count; i++)
{
var p1 = pointList[i - 1];
var p2 = pointList[i];
var inside = Intersections.GetIntersections(ref p1, ref p2, viewport);
if (inside)
{
if (figure == null)
{
figure = new PathFigure
{
StartPoint = p1,
IsClosed = false,
IsFilled = false
};
segment = new PolyLineSegment();
figure.Segments.Add(segment);
pathFigures.Add(figure);
}
segment.Points.Add(p2);
}
if (!inside || p2 != pointList[i])
{
figure = null;
}
}
}
}
}
#endregion
}
}

View file

@ -1,96 +0,0 @@
// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2021 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL)
using System;
#if WINUI
using XamlMedia = Microsoft.UI.Xaml.Media;
#else
using XamlMedia = Windows.UI.Xaml.Media;
#endif
namespace MapControl
{
/// <summary>
/// Replaces Windows.UI.Xaml.Media.Matrix to achieve necessary floating point precision.
/// </summary>
public struct Matrix
{
public double M11 { get; set; }
public double M12 { get; set; }
public double M21 { get; set; }
public double M22 { get; set; }
public double OffsetX { get; set; }
public double OffsetY { get; set; }
public Matrix(double m11, double m12, double m21, double m22, double offsetX, double offsetY)
{
M11 = m11;
M12 = m12;
M21 = m21;
M22 = m22;
OffsetX = offsetX;
OffsetY = offsetY;
}
public static implicit operator XamlMedia.Matrix(Matrix m)
{
return new XamlMedia.Matrix(m.M11, m.M12, m.M21, m.M22, m.OffsetX, m.OffsetY);
}
public Point Transform(Point p)
{
return new Point(M11 * p.X + M12 * p.Y + OffsetX, M21 * p.X + M22 * p.Y + OffsetY);
}
public void Translate(double x, double y)
{
OffsetX += x;
OffsetY += y;
}
public void Rotate(double angle)
{
angle = (angle % 360d) / 180d * Math.PI;
if (angle != 0d)
{
var cos = Math.Cos(angle);
var sin = Math.Sin(angle);
SetMatrix(
cos * M11 - sin * M12,
sin * M11 + cos * M12,
cos * M21 - sin * M22,
sin * M21 + cos * M22,
cos * OffsetX - sin * OffsetY,
sin * OffsetX + cos * OffsetY);
}
}
public void Invert()
{
var invDet = 1d / (M11 * M22 - M12 * M21);
if (double.IsInfinity(invDet))
{
throw new InvalidOperationException("Matrix is not invertible.");
}
SetMatrix(
invDet * M22, invDet * -M12, invDet * -M21, invDet * M11,
invDet * (M21 * OffsetY - M22 * OffsetX),
invDet * (M12 * OffsetX - M11 * OffsetY));
}
private void SetMatrix(double m11, double m12, double m21, double m22, double offsetX, double offsetY)
{
M11 = m11;
M12 = m12;
M21 = m21;
M22 = m22;
OffsetX = offsetX;
OffsetY = offsetY;
}
}
}

View file

@ -1,76 +0,0 @@
// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2021 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL)
namespace MapControl
{
/// <summary>
/// Replaces Windows.Foundation.Point to achieve necessary floating point precision.
/// </summary>
public struct Point
{
public double X { get; set; }
public double Y { get; set; }
public Point(double x, double y)
{
X = x;
Y = y;
}
public static implicit operator Windows.Foundation.Point(Point p)
{
return new Windows.Foundation.Point(p.X, p.Y);
}
public static implicit operator Point(Windows.Foundation.Point p)
{
return new Point(p.X, p.Y);
}
public static explicit operator Point(Vector v)
{
return new Point(v.X, v.Y);
}
public static Point operator -(Point p)
{
return new Point(-p.X, -p.Y);
}
public static Point operator +(Point p, Vector v)
{
return new Point(p.X + v.X, p.Y + v.Y);
}
public static Point operator -(Point p, Vector v)
{
return new Point(p.X - v.X, p.Y - v.Y);
}
public static Vector operator -(Point p1, Point p2)
{
return new Vector(p1.X - p2.X, p1.Y - p2.Y);
}
public static bool operator ==(Point p1, Point p2)
{
return p1.X == p2.X && p1.Y == p2.Y;
}
public static bool operator !=(Point p1, Point p2)
{
return !(p1 == p2);
}
public override bool Equals(object o)
{
return o is Point && this == (Point)o;
}
public override int GetHashCode()
{
return X.GetHashCode() ^ Y.GetHashCode();
}
}
}

View file

@ -7,8 +7,8 @@ using System.Runtime.InteropServices;
[assembly: AssemblyCompany("Clemens Fischer")]
[assembly: AssemblyCopyright("Copyright © 2021 Clemens Fischer")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyVersion("6.1.0")]
[assembly: AssemblyFileVersion("6.1.0")]
[assembly: AssemblyVersion("7.0.0")]
[assembly: AssemblyFileVersion("7.0.0")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCulture("")]
[assembly: ComVisible(false)]

View file

@ -1,64 +0,0 @@
// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2021 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL)
using System;
#if WINUI
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Media.Imaging;
#else
using Windows.UI.Xaml;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Media.Imaging;
#endif
namespace MapControl
{
public partial class Tile
{
public void SetImage(ImageSource image, bool fadeIn = true)
{
Pending = false;
if (image != null && fadeIn && MapBase.ImageFadeDuration > TimeSpan.Zero)
{
if (image is BitmapImage bitmap && bitmap.UriSource != null)
{
bitmap.ImageOpened += BitmapImageOpened;
bitmap.ImageFailed += BitmapImageFailed;
}
else
{
FadeIn();
}
}
else
{
Image.Opacity = 1d;
}
Image.Source = image;
}
private void BitmapImageOpened(object sender, RoutedEventArgs e)
{
var bitmap = (BitmapImage)sender;
bitmap.ImageOpened -= BitmapImageOpened;
bitmap.ImageFailed -= BitmapImageFailed;
FadeIn();
}
private void BitmapImageFailed(object sender, ExceptionRoutedEventArgs e)
{
var bitmap = (BitmapImage)sender;
bitmap.ImageOpened -= BitmapImageOpened;
bitmap.ImageFailed -= BitmapImageFailed;
Image.Source = null;
}
}
}

View file

@ -1,83 +0,0 @@
// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2021 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL)
namespace MapControl
{
public struct Vector
{
public double X { get; set; }
public double Y { get; set; }
public Vector(double x, double y)
{
X = x;
Y = y;
}
public static implicit operator Windows.Foundation.Point(Vector v)
{
return new Windows.Foundation.Point(v.X, v.Y);
}
public static implicit operator Vector(Windows.Foundation.Point v)
{
return new Vector(v.X, v.Y);
}
public static explicit operator Vector(Point p)
{
return new Vector(p.X, p.Y);
}
public static Vector operator -(Vector v)
{
return new Vector(-v.X, -v.Y);
}
public static Point operator +(Vector v, Point p)
{
return new Point(v.X + p.X, v.Y + p.Y);
}
public static Vector operator +(Vector v1, Vector v2)
{
return new Vector(v1.X + v2.X, v1.Y + v2.Y);
}
public static Vector operator -(Vector v1, Vector v2)
{
return new Vector(v1.X - v2.X, v1.Y - v2.Y);
}
public static Vector operator *(double f, Vector v)
{
return new Vector(f * v.X, f * v.Y);
}
public static Vector operator *(Vector v, double f)
{
return new Vector(f * v.X, f * v.Y);
}
public static bool operator ==(Vector v1, Vector v2)
{
return v1.X == v2.X && v1.Y == v2.Y;
}
public static bool operator !=(Vector v1, Vector v2)
{
return !(v1 == v2);
}
public override bool Equals(object o)
{
return o is Vector && this == (Vector)o;
}
public override int GetHashCode()
{
return X.GetHashCode() ^ Y.GetHashCode();
}
}
}