Version 4: Upgrade to VS 2017

This commit is contained in:
ClemensF 2017-08-04 21:38:58 +02:00
parent 2aafe32e00
commit ec47f225b3
142 changed files with 1828 additions and 18384 deletions

View file

@ -0,0 +1,54 @@
// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2017 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL)
using System.Collections.Generic;
using Windows.Foundation;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Media.Animation;
namespace MapControl
{
internal static 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;
BeginAnimation(obj, property, (Timeline)animation);
}
public static void BeginAnimation(this DependencyObject obj, DependencyProperty property, PointAnimation animation)
{
animation.EnableDependentAnimation = true;
BeginAnimation(obj, property, (Timeline)animation);
}
private static Dictionary<DependencyProperty, string> properties = new Dictionary<DependencyProperty, string>()
{
{ UIElement.OpacityProperty, "Opacity" },
{ MapBase.ZoomLevelProperty, "ZoomLevel" },
{ MapBase.HeadingProperty, "Heading" },
{ MapBase.CenterPointProperty, "CenterPoint" }
};
private static void BeginAnimation(DependencyObject obj, DependencyProperty property, Timeline animation)
{
string propertyName;
if (properties.TryGetValue(property, out propertyName))
{
Storyboard.SetTargetProperty(animation, propertyName);
Storyboard.SetTarget(animation, obj);
var storyboard = new Storyboard();
storyboard.Children.Add(animation);
storyboard.Begin();
}
}
}
}

View file

@ -0,0 +1,22 @@
// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2017 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL)
using System;
using System.Threading.Tasks;
using Windows.Storage.Streams;
namespace MapControl.Caching
{
public class ImageCacheItem
{
public IBuffer Buffer { get; set; }
public DateTime Expiration { get; set; }
}
public interface IImageCache
{
Task<ImageCacheItem> GetAsync(string key);
Task SetAsync(string key, IBuffer buffer, DateTime expiration);
}
}

View file

@ -0,0 +1,99 @@
// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2017 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL)
using System;
using System.Diagnostics;
using System.IO;
using System.Threading.Tasks;
using Windows.Storage;
using Windows.Storage.Streams;
namespace MapControl.Caching
{
public class ImageFileCache : IImageCache
{
private StorageFolder rootFolder;
public ImageFileCache(StorageFolder rootFolder)
{
if (rootFolder == null)
{
throw new ArgumentNullException("The parameter rootFolder must not be null.");
}
this.rootFolder = rootFolder;
Debug.WriteLine("Created ImageFileCache in " + rootFolder.Path);
}
public virtual async Task<ImageCacheItem> GetAsync(string key)
{
string path = null;
try
{
path = Path.Combine(key.Split('\\', '/', ':', ';'));
}
catch (Exception ex)
{
Debug.WriteLine("ImageFileCache: Invalid key {0}: {1}", key, ex.Message);
}
if (path != null)
{
var item = await rootFolder.TryGetItemAsync(path);
if (item != null && item.IsOfType(StorageItemTypes.File))
{
var file = (StorageFile)item;
//Debug.WriteLine("ImageFileCache: Reading " + file.Path);
try
{
return new ImageCacheItem
{
Buffer = await FileIO.ReadBufferAsync(file),
Expiration = (await file.Properties.GetImagePropertiesAsync()).DateTaken.UtcDateTime
};
}
catch (Exception ex)
{
Debug.WriteLine("ImageFileCache: Reading {0}: {1}", file.Path, ex.Message);
}
}
}
return null;
}
public virtual async Task SetAsync(string key, IBuffer buffer, DateTime expiration)
{
var paths = key.Split('\\', '/', ':', ';');
try
{
var folder = rootFolder;
for (int i = 0; i < paths.Length - 1; i++)
{
folder = await folder.CreateFolderAsync(paths[i], CreationCollisionOption.OpenIfExists);
}
var file = await folder.CreateFileAsync(paths[paths.Length - 1], CreationCollisionOption.ReplaceExisting);
//Debug.WriteLine("ImageFileCache: Writing {0}, Expires {1}", file.Path, expiration.ToLocalTime());
await FileIO.WriteBufferAsync(file, buffer);
// Store expiration date in ImageProperties.DateTaken
var properties = await file.Properties.GetImagePropertiesAsync();
properties.DateTaken = expiration;
await properties.SavePropertiesAsync();
}
catch (Exception ex)
{
Debug.WriteLine("ImageFileCache: Writing {0}\\{1}: {2}", rootFolder.Path, string.Join("\\", paths), ex.Message);
}
}
}
}

75
MapControl/UWP/Map.UWP.cs Normal file
View file

@ -0,0 +1,75 @@
// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2017 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL)
using System;
using Windows.Foundation;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Input;
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));
private bool transformPending;
private Point transformTranslation;
private double transformRotation;
private double transformScale = 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.
/// </summary>
public double MouseWheelZoomDelta
{
get { return (double)GetValue(MouseWheelZoomDeltaProperty); }
set { SetValue(MouseWheelZoomDeltaProperty, value); }
}
protected virtual void OnPointerWheelChanged(object sender, PointerRoutedEventArgs e)
{
var point = e.GetCurrentPoint(this);
var zoomChange = MouseWheelZoomDelta * point.Properties.MouseWheelDelta / 120d;
ZoomMap(point.Position, TargetZoomLevel + zoomChange);
}
protected virtual async void OnManipulationDelta(object sender, ManipulationDeltaRoutedEventArgs e)
{
transformTranslation.X += e.Delta.Translation.X;
transformTranslation.Y += e.Delta.Translation.Y;
transformRotation += e.Delta.Rotation;
transformScale *= e.Delta.Scale;
if (!transformPending)
{
transformPending = true;
await Dispatcher.RunIdleAsync(a =>
{
TransformMap(e.Position, transformTranslation, transformRotation, transformScale);
transformPending = false;
transformTranslation.X = 0d;
transformTranslation.Y = 0d;
transformRotation = 0d;
transformScale = 1d;
});
}
}
}
}

View file

@ -0,0 +1,64 @@
// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2017 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL)
using Windows.Foundation;
using Windows.UI;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Media;
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)));
partial void Initialize()
{
// 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.Transparent)));
Style = style;
var clip = new RectangleGeometry();
Clip = clip;
SizeChanged += (s, e) =>
{
if (clip.Rect.Width != e.NewSize.Width || clip.Rect.Height != e.NewSize.Height)
{
clip.Rect = new Rect(0d, 0d, e.NewSize.Width, e.NewSize.Height);
ResetTransformCenter();
UpdateTransform();
}
};
}
}
}

View file

@ -0,0 +1,180 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{951BC5D2-D653-42D9-9A91-21DC50DE0182}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>MapControl</RootNamespace>
<AssemblyName>MapControl.UWP</AssemblyName>
<DefaultLanguage>en-US</DefaultLanguage>
<TargetPlatformIdentifier>UAP</TargetPlatformIdentifier>
<TargetPlatformVersion>10.0.10240.0</TargetPlatformVersion>
<TargetPlatformMinVersion>10.0.10240.0</TargetPlatformMinVersion>
<MinimumVisualStudioVersion>14</MinimumVisualStudioVersion>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>..\bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<GenerateLibraryLayout>true</GenerateLibraryLayout>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>none</DebugType>
<Optimize>true</Optimize>
<OutputPath>..\bin\Release\</OutputPath>
<DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<GenerateLibraryLayout>true</GenerateLibraryLayout>
</PropertyGroup>
<ItemGroup>
<!-- A reference to the entire .Net Framework and Windows SDK are automatically included -->
<None Include="..\..\MapControl.snk">
<Link>MapControl.snk</Link>
</None>
<None Include="project.json" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\Shared\AzimuthalEquidistantProjection.cs">
<Link>AzimuthalEquidistantProjection.cs</Link>
</Compile>
<Compile Include="..\Shared\AzimuthalProjection.cs">
<Link>AzimuthalProjection.cs</Link>
</Compile>
<Compile Include="..\Shared\BingMapsTileLayer.cs">
<Link>BingMapsTileLayer.cs</Link>
</Compile>
<Compile Include="..\Shared\BingMapsTileSource.cs">
<Link>BingMapsTileSource.cs</Link>
</Compile>
<Compile Include="..\Shared\BoundingBox.cs">
<Link>BoundingBox.cs</Link>
</Compile>
<Compile Include="..\Shared\CenteredBoundingBox.cs">
<Link>CenteredBoundingBox.cs</Link>
</Compile>
<Compile Include="..\Shared\EquirectangularProjection.cs">
<Link>EquirectangularProjection.cs</Link>
</Compile>
<Compile Include="..\Shared\GnomonicProjection.cs">
<Link>GnomonicProjection.cs</Link>
</Compile>
<Compile Include="..\Shared\HyperlinkText.cs">
<Link>HyperlinkText.cs</Link>
</Compile>
<Compile Include="..\Shared\Location.cs">
<Link>Location.cs</Link>
</Compile>
<Compile Include="..\Shared\LocationCollection.cs">
<Link>LocationCollection.cs</Link>
</Compile>
<Compile Include="..\Shared\MapBase.cs">
<Link>MapBase.cs</Link>
</Compile>
<Compile Include="..\Shared\MapGraticule.cs">
<Link>MapGraticule.cs</Link>
</Compile>
<Compile Include="..\Shared\MapImageLayer.cs">
<Link>MapImageLayer.cs</Link>
</Compile>
<Compile Include="..\Shared\MapOverlay.cs">
<Link>MapOverlay.cs</Link>
</Compile>
<Compile Include="..\Shared\MapPanel.cs">
<Link>MapPanel.cs</Link>
</Compile>
<Compile Include="..\Shared\MapPath.cs">
<Link>MapPath.cs</Link>
</Compile>
<Compile Include="..\Shared\MapPolyline.cs">
<Link>MapPolyline.cs</Link>
</Compile>
<Compile Include="..\Shared\MapProjection.cs">
<Link>MapProjection.cs</Link>
</Compile>
<Compile Include="..\Shared\MapTileLayer.cs">
<Link>MapTileLayer.cs</Link>
</Compile>
<Compile Include="..\Shared\OrthographicProjection.cs">
<Link>OrthographicProjection.cs</Link>
</Compile>
<Compile Include="..\Shared\StereographicProjection.cs">
<Link>StereographicProjection.cs</Link>
</Compile>
<Compile Include="..\Shared\Tile.cs">
<Link>Tile.cs</Link>
</Compile>
<Compile Include="..\Shared\TileGrid.cs">
<Link>TileGrid.cs</Link>
</Compile>
<Compile Include="..\Shared\TileImageLoader.cs">
<Link>TileImageLoader.cs</Link>
</Compile>
<Compile Include="..\Shared\TileSource.cs">
<Link>TileSource.cs</Link>
</Compile>
<Compile Include="..\Shared\ViewportChangedEventArgs.cs">
<Link>ViewportChangedEventArgs.cs</Link>
</Compile>
<Compile Include="..\Shared\WebMercatorProjection.cs">
<Link>WebMercatorProjection.cs</Link>
</Compile>
<Compile Include="..\Shared\WmsImageLayer.cs">
<Link>WmsImageLayer.cs</Link>
</Compile>
<Compile Include="Extensions.UWP.cs" />
<Compile Include="ImageCache.UWP.cs" />
<Compile Include="ImageFileCache.UWP.cs" />
<Compile Include="Map.UWP.cs" />
<Compile Include="MapBase.UWP.cs" />
<Compile Include="MapGraticule.UWP.cs" />
<Compile Include="MapImageLayer.UWP.cs" />
<Compile Include="MapItem.UWP.cs" />
<Compile Include="MapItemsControl.UWP.cs" />
<Compile Include="MapOverlay.UWP.cs" />
<Compile Include="MapPanel.UWP.cs" />
<Compile Include="MapPath.UWP.cs" />
<Compile Include="MapPolyline.UWP.cs" />
<Compile Include="MapTileLayer.UWP.cs" />
<Compile Include="MatrixEx.UWP.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Pushpin.UWP.cs" />
<Compile Include="Tile.UWP.cs" />
<Compile Include="TileImageLoader.UWP.cs" />
<EmbeddedResource Include="Properties\MapControl.UWP.rd.xml" />
</ItemGroup>
<ItemGroup>
<Page Include="Themes\Generic.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
</ItemGroup>
<PropertyGroup Condition=" '$(VisualStudioVersion)' == '' or '$(VisualStudioVersion)' &lt; '14.0' ">
<VisualStudioVersion>14.0</VisualStudioVersion>
</PropertyGroup>
<PropertyGroup>
<SignAssembly>true</SignAssembly>
</PropertyGroup>
<PropertyGroup>
<AssemblyOriginatorKeyFile>..\..\MapControl.snk</AssemblyOriginatorKeyFile>
</PropertyGroup>
<Import Project="$(MSBuildExtensionsPath)\Microsoft\WindowsXaml\v$(VisualStudioVersion)\Microsoft.Windows.UI.Xaml.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View file

@ -0,0 +1,208 @@
// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2017 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL)
using System;
using Windows.Foundation;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Shapes;
using Windows.UI.Xaml.Data;
namespace MapControl
{
public partial class MapGraticule
{
private Path path;
private Location graticuleStart;
private Location graticuleEnd;
public MapGraticule()
{
IsHitTestVisible = false;
StrokeThickness = 0.5;
}
protected override void OnViewportChanged(ViewportChangedEventArgs e)
{
var projection = ParentMap.MapProjection;
if (!double.IsNaN(projection.LongitudeScale))
{
if (path == null)
{
path = new Path
{
Data = new PathGeometry()
};
path.SetBinding(Shape.StrokeProperty,
GetBindingExpression(StrokeProperty)?.ParentBinding ??
new Binding
{
Source = this,
Path = new PropertyPath("Stroke")
});
path.SetBinding(Shape.StrokeThicknessProperty,
GetBindingExpression(StrokeThicknessProperty)?.ParentBinding ??
new Binding
{
Source = this,
Path = new PropertyPath("StrokeThickness")
});
Children.Add(path);
}
var bounds = projection.ViewportRectToBoundingBox(new Rect(0d, 0d, ParentMap.RenderSize.Width, ParentMap.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);
if (!lineStart.Equals(graticuleStart) || !lineEnd.Equals(graticuleEnd))
{
graticuleStart = lineStart;
graticuleEnd = lineEnd;
var geometry = (PathGeometry)path.Data;
geometry.Figures.Clear();
geometry.Transform = projection.ViewportTransform;
for (var lat = labelStart.Latitude; lat <= bounds.North; lat += lineDistance)
{
var figure = new PathFigure
{
StartPoint = projection.LocationToPoint(new Location(lat, lineStart.Longitude)),
IsClosed = false,
IsFilled = false
};
figure.Segments.Add(new LineSegment
{
Point = projection.LocationToPoint(new Location(lat, lineEnd.Longitude)),
});
geometry.Figures.Add(figure);
}
for (var lon = labelStart.Longitude; lon <= bounds.East; lon += lineDistance)
{
var figure = new PathFigure
{
StartPoint = projection.LocationToPoint(new Location(lineStart.Latitude, lon)),
IsClosed = false,
IsFilled = false
};
figure.Segments.Add(new LineSegment
{
Point = projection.LocationToPoint(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
{
var renderTransform = new TransformGroup();
renderTransform.Children.Add(new TranslateTransform());
renderTransform.Children.Add(ParentMap.RotateTransform);
renderTransform.Children.Add(new TranslateTransform());
label = new TextBlock
{
RenderTransform = renderTransform
};
label.SetBinding(TextBlock.ForegroundProperty,
GetBindingExpression(ForegroundProperty)?.ParentBinding ??
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 = 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));
var translateTransform = (TranslateTransform)((TransformGroup)label.RenderTransform).Children[0];
translateTransform.X = StrokeThickness / 2d + 2d;
translateTransform.Y = -label.DesiredSize.Height / 2d;
}
}
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 viewportTransform = (TranslateTransform)((TransformGroup)label.RenderTransform).Children[2];
var viewportPosition = projection.LocationToViewportPoint(location);
viewportTransform.X = viewportPosition.X;
viewportTransform.Y = viewportPosition.Y;
}
}
else if (path != null)
{
path = null;
graticuleStart = null;
graticuleEnd = null;
Children.Clear();
}
base.OnViewportChanged(e);
}
}
}

View file

@ -0,0 +1,57 @@
// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2017 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL)
using System;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media.Imaging;
namespace MapControl
{
public partial class MapImageLayer
{
protected void UpdateImage(Uri uri)
{
UpdateImage(uri != null ? new BitmapImage(uri) : null);
}
protected void UpdateImage(BitmapSource bitmapSource)
{
SetTopImage(bitmapSource);
var bitmapImage = bitmapSource as BitmapImage;
if (bitmapImage != null)
{
bitmapImage.ImageOpened += BitmapImageOpened;
bitmapImage.ImageFailed += BitmapImageFailed;
}
else
{
SwapImages();
}
}
private void BitmapImageOpened(object sender, RoutedEventArgs e)
{
var bitmapImage = (BitmapImage)sender;
bitmapImage.ImageOpened -= BitmapImageOpened;
bitmapImage.ImageFailed -= BitmapImageFailed;
SwapImages();
}
private void BitmapImageFailed(object sender, ExceptionRoutedEventArgs e)
{
var bitmapImage = (BitmapImage)sender;
bitmapImage.ImageOpened -= BitmapImageOpened;
bitmapImage.ImageFailed -= BitmapImageFailed;
((Image)Children[topImageIndex]).Source = null;
SwapImages();
}
}
}

View file

@ -0,0 +1,26 @@
// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2017 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL)
using Windows.UI.Xaml.Controls;
namespace MapControl
{
/// <summary>
/// Container class for an item in a MapItemsControl.
/// </summary>
public class MapItem : ListBoxItem
{
public MapItem()
{
DefaultStyleKey = typeof(MapItem);
MapPanel.AddParentMapHandlers(this);
}
public Location Location
{
get { return (Location)GetValue(MapPanel.LocationProperty); }
set { SetValue(MapPanel.LocationProperty, value); }
}
}
}

View file

@ -0,0 +1,31 @@
// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2017 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL)
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
namespace MapControl
{
/// <summary>
/// Manages a collection of selectable items on a Map. Uses MapItem as item container class.
/// </summary>
public class MapItemsControl : ListBox
{
public MapItemsControl()
{
DefaultStyleKey = typeof(MapItemsControl);
MapPanel.AddParentMapHandlers(this);
}
protected override DependencyObject GetContainerForItemOverride()
{
return new MapItem();
}
protected override bool IsItemItsOwnContainerOverride(object item)
{
return item is MapItem;
}
}
}

View file

@ -0,0 +1,95 @@
// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2017 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL)
using Windows.UI.Text;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Media;
namespace MapControl
{
public partial class MapOverlay
{
public static readonly DependencyProperty FontSizeProperty = DependencyProperty.Register(
nameof(FontSize), typeof(double), typeof(MapOverlay), new PropertyMetadata(10d));
public static readonly DependencyProperty FontFamilyProperty = DependencyProperty.Register(
nameof(FontFamily), typeof(FontFamily), typeof(MapOverlay), new PropertyMetadata(null));
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));
protected override void SetParentMapOverride(MapBase parentMap)
{
if (GetBindingExpression(ForegroundProperty) != null)
{
ClearValue(ForegroundProperty);
}
if (GetBindingExpression(StrokeProperty) != null)
{
ClearValue(StrokeProperty);
}
if (parentMap != null)
{
if (Foreground == null)
{
SetBinding(ForegroundProperty, new Binding
{
Source = parentMap,
Path = new PropertyPath("Foreground")
});
}
if (Stroke == null)
{
SetBinding(StrokeProperty, new Binding
{
Source = parentMap,
Path = new PropertyPath("Foreground")
});
}
}
base.SetParentMapOverride(parentMap);
}
}
}

View file

@ -0,0 +1,68 @@
// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2017 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL)
using Windows.UI.Xaml;
using Windows.UI.Xaml.Media;
namespace MapControl
{
public partial class MapPanel
{
public static readonly DependencyProperty ParentMapProperty = DependencyProperty.RegisterAttached(
"ParentMap", typeof(MapBase), typeof(MapPanel), new PropertyMetadata(null, ParentMapPropertyChanged));
public MapPanel()
{
if (this is MapBase)
{
SetValue(ParentMapProperty, this);
}
else
{
AddParentMapHandlers(this);
}
}
/// <summary>
/// Helper method to work around missing property value inheritance in Silverlight and Windows Runtime.
/// Adds Loaded and Unloaded event handlers to the specified FrameworkElement, which set and clear the
/// value of the MapPanel.ParentMap attached property.
/// </summary>
public static void AddParentMapHandlers(FrameworkElement element)
{
element.Loaded += (s, e) => GetParentMap(element);
element.Unloaded += (s, e) => element.ClearValue(ParentMapProperty);
}
public static MapBase GetParentMap(UIElement element)
{
var parentMap = (MapBase)element.GetValue(ParentMapProperty);
if (parentMap == null && (parentMap = FindParentMap(element)) != null)
{
element.SetValue(ParentMapProperty, parentMap);
}
return parentMap;
}
private static MapBase FindParentMap(UIElement element)
{
MapBase parentMap = null;
var parentElement = VisualTreeHelper.GetParent(element) as UIElement;
if (parentElement != null)
{
parentMap = parentElement as MapBase;
if (parentMap == null)
{
parentMap = GetParentMap(parentElement);
}
}
return parentMap;
}
}
}

View file

@ -0,0 +1,46 @@
// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2017 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL)
using Windows.Foundation;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Shapes;
namespace MapControl
{
public partial class MapPath : Path
{
private Geometry data;
public MapPath()
{
MapPanel.AddParentMapHandlers(this);
}
protected override Size MeasureOverride(Size availableSize)
{
if (Stretch != Stretch.None)
{
Stretch = Stretch.None;
}
// Workaround for missing PropertyChangedCallback for the Data property.
if (data != Data)
{
if (data != null)
{
data.ClearValue(Geometry.TransformProperty);
}
data = Data;
if (data != null)
{
data.Transform = viewportTransform;
}
}
return new Size();
}
}
}

View file

@ -0,0 +1,61 @@
// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2017 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL)
using System.Collections.Generic;
using System.Linq;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Media;
namespace MapControl
{
public partial class MapPolyline
{
public static readonly DependencyProperty FillRuleProperty = DependencyProperty.Register(
nameof(FillRule), typeof(FillRule), typeof(MapPolyline),
new PropertyMetadata(FillRule.EvenOdd, (o, e) => ((PathGeometry)((MapPolyline)o).Data).FillRule = (FillRule)e.NewValue));
public MapPolyline()
{
Data = new PathGeometry();
}
/// <summary>
/// Gets or sets the locations that define the polyline points.
/// </summary>
public IEnumerable<Location> Locations
{
get { return (IEnumerable<Location>)GetValue(LocationsProperty); }
set { SetValue(LocationsProperty, value); }
}
protected override void UpdateData()
{
var geometry = (PathGeometry)Data;
geometry.Figures.Clear();
if (ParentMap != null && Locations != null && Locations.Any())
{
var points = Locations.Select(l => ParentMap.MapProjection.LocationToPoint(l));
var figure = new PathFigure
{
StartPoint = points.First(),
IsClosed = IsClosed,
IsFilled = IsClosed
};
var segment = new PolyLineSegment();
foreach (var point in points.Skip(1))
{
segment.Points.Add(point);
}
figure.Segments.Add(segment);
geometry.Figures.Add(figure);
}
}
}
}

View file

@ -0,0 +1,19 @@
// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2017 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL)
using Windows.UI.Xaml.Markup;
namespace MapControl
{
[ContentProperty(Name = "TileSource")]
public partial class MapTileLayer
{
partial void Initialize()
{
IsHitTestVisible = false;
MapPanel.AddParentMapHandlers(this);
}
}
}

View file

@ -0,0 +1,87 @@
// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2017 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL)
using System;
using Windows.UI.Xaml.Media;
namespace MapControl
{
internal static class MatrixEx
{
/// <summary>
/// Used in MapProjection and MapTileLayer.
/// </summary>
public static Matrix TranslateScaleRotateTranslate(
double translation1X, double translation1Y,
double scaleX, double scaleY, double rotationAngle,
double translation2X, double translation2Y)
{
var matrix = new Matrix(
scaleX, 0d, 0d, scaleY,
scaleX * translation1X,
scaleY * translation1Y);
if (rotationAngle != 0d)
{
rotationAngle = (rotationAngle % 360d) / 180d * Math.PI;
var cos = Math.Cos(rotationAngle);
var sin = Math.Sin(rotationAngle);
matrix = new Matrix(
matrix.M11 * cos - matrix.M12 * sin,
matrix.M11 * sin + matrix.M12 * cos,
matrix.M21 * cos - matrix.M22 * sin,
matrix.M21 * sin + matrix.M22 * cos,
cos * matrix.OffsetX - sin * matrix.OffsetY,
sin * matrix.OffsetX + cos * matrix.OffsetY);
}
matrix.OffsetX += translation2X;
matrix.OffsetY += translation2Y;
return matrix;
}
public static Matrix TranslateScaleRotateTranslate_(
double translation1X, double translation1Y,
double scaleX, double scaleY, double rotationAngle,
double translation2X, double translation2Y)
{
var m11 = scaleX;
var m12 = 0d;
var m21 = 0d;
var m22 = scaleY;
var offsetX = scaleX * translation1X;
var offsetY = scaleY * translation1Y;
if (rotationAngle != 0d)
{
rotationAngle = (rotationAngle % 360d) / 180d * Math.PI;
var cos = Math.Cos(rotationAngle);
var sin = Math.Sin(rotationAngle);
var _m11 = m11;
var _m12 = m12;
var _m21 = m21;
var _m22 = m22;
var _offsetX = offsetX;
var _offsetY = offsetY;
m11 = _m11 * cos - _m12 * sin;
m12 = _m11 * sin + _m12 * cos;
m21 = _m21 * cos - _m22 * sin;
m22 = _m21 * sin + _m22 * cos;
offsetX = cos * _offsetX - sin * _offsetY;
offsetY = sin * _offsetX + cos * _offsetY;
}
offsetX += translation2X;
offsetY += translation2Y;
return new Matrix(m11, m12, m21, m22, offsetX, offsetY);
}
}
}

View file

@ -0,0 +1,14 @@
using System.Reflection;
using System.Runtime.InteropServices;
[assembly: AssemblyTitle("XAML Map Control for UWP")]
[assembly: AssemblyDescription("XAML Map Control Library for UWP")]
[assembly: AssemblyProduct("XAML Map Control")]
[assembly: AssemblyCompany("Clemens Fischer")]
[assembly: AssemblyCopyright("© 2017 Clemens Fischer")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyVersion("4.0.0")]
[assembly: AssemblyFileVersion("4.0.0")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCulture("")]
[assembly: ComVisible(false)]

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<Directives xmlns="http://schemas.microsoft.com/netfx/2013/01/metadata">
<Library Name="MapControl.UWP">
</Library>
</Directives>

View file

@ -0,0 +1,26 @@
// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2017 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL)
using Windows.UI.Xaml.Controls;
namespace MapControl
{
/// <summary>
/// Displays a pushpin at a geographic location provided by the MapPanel.Location attached property.
/// </summary>
public class Pushpin : ContentControl
{
public Pushpin()
{
DefaultStyleKey = typeof(Pushpin);
MapPanel.AddParentMapHandlers(this);
}
public Location Location
{
get { return (Location)GetValue(MapPanel.LocationProperty); }
set { SetValue(MapPanel.LocationProperty, value); }
}
}
}

View file

@ -0,0 +1,65 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:map="using:MapControl">
<Style TargetType="map:MapItemsControl">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<ItemsPresenter/>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<map:MapPanel/>
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="map:MapItem">
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="VerticalAlignment" Value="Stretch"/>
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="VerticalContentAlignment" Value="Stretch"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="map:MapItem">
<ContentPresenter Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
Margin="{TemplateBinding Padding}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="map:Pushpin">
<Setter Property="HorizontalAlignment" Value="Left"/>
<Setter Property="VerticalAlignment" Value="Bottom"/>
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="VerticalContentAlignment" Value="Stretch"/>
<Setter Property="Padding" Value="3"/>
<Setter Property="Foreground" Value="Black"/>
<Setter Property="Background" Value="White"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="map:Pushpin">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Rectangle Fill="{TemplateBinding Background}"/>
<Path Grid.Row="1" Fill="{TemplateBinding Background}" Data="M 0,-0.5 L 0,16 16,-0.5"/>
<ContentPresenter Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
Margin="{TemplateBinding Padding}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>

View file

@ -0,0 +1,64 @@
// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2017 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL)
using System;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Media.Animation;
using Windows.UI.Xaml.Media.Imaging;
namespace MapControl
{
public partial class Tile
{
public void SetImage(ImageSource imageSource, bool fadeIn = true)
{
Pending = false;
if (fadeIn && FadeDuration > TimeSpan.Zero)
{
var bitmapImage = imageSource as BitmapImage;
if (bitmapImage != null && bitmapImage.UriSource != null)
{
bitmapImage.ImageOpened += BitmapImageOpened;
bitmapImage.ImageFailed += BitmapImageFailed;
}
else
{
Image.BeginAnimation(UIElement.OpacityProperty,
new DoubleAnimation { From = 0d, To = 1d, Duration = FadeDuration });
}
}
else
{
Image.Opacity = 1d;
}
Image.Source = imageSource;
}
private void BitmapImageOpened(object sender, RoutedEventArgs e)
{
var bitmapImage = (BitmapImage)sender;
bitmapImage.ImageOpened -= BitmapImageOpened;
bitmapImage.ImageFailed -= BitmapImageFailed;
Image.BeginAnimation(UIElement.OpacityProperty,
new DoubleAnimation { From = 0d, To = 1d, Duration = FadeDuration });
}
private void BitmapImageFailed(object sender, ExceptionRoutedEventArgs e)
{
var bitmapImage = (BitmapImage)sender;
bitmapImage.ImageOpened -= BitmapImageOpened;
bitmapImage.ImageFailed -= BitmapImageFailed;
Image.Source = null;
}
}
}

View file

@ -0,0 +1,114 @@
// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2017 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL)
using System;
using System.Diagnostics;
using System.Threading.Tasks;
using Windows.Storage;
using Windows.Storage.Streams;
using Windows.UI.Core;
using Windows.UI.Xaml.Media.Imaging;
using Windows.Web.Http;
namespace MapControl
{
public partial class TileImageLoader : ITileImageLoader
{
/// <summary>
/// Default StorageFolder where an IImageCache instance may save cached data.
/// </summary>
public static readonly StorageFolder DefaultCacheFolder = ApplicationData.Current.TemporaryFolder;
/// <summary>
/// The IImageCache implementation used to cache tile images. The default is null.
/// </summary>
public static Caching.IImageCache Cache { get; set; }
private async Task LoadTileImageAsync(Tile tile, Uri uri, string cacheKey)
{
var cacheItem = await Cache.GetAsync(cacheKey);
var buffer = cacheItem?.Buffer;
var loaded = false;
//if (buffer != null)
//{
// Debug.WriteLine("TileImageLoader: {0}: expire{1} {2}", cacheKey, cacheItem.Expiration < DateTime.UtcNow ? "d" : "s", cacheItem.Expiration);
//}
if (buffer == null || cacheItem.Expiration < DateTime.UtcNow)
{
loaded = await DownloadTileImageAsync(tile, uri, cacheKey);
}
if (!loaded && buffer != null) // keep expired image if download failed
{
await SetTileImageAsync(tile, buffer);
}
}
private async Task<bool> DownloadTileImageAsync(Tile tile, Uri uri, string cacheKey)
{
try
{
using (var response = await HttpClient.GetAsync(uri))
{
if (response.IsSuccessStatusCode)
{
string tileInfo;
if (!response.Headers.TryGetValue(bingMapsTileInfo, out tileInfo) ||
tileInfo != bingMapsNoTile)
{
var buffer = await response.Content.ReadAsBufferAsync();
await SetTileImageAsync(tile, buffer); // create BitmapImage in UI thread before caching
await Cache.SetAsync(cacheKey, buffer, GetExpiration(response));
}
return true;
}
Debug.WriteLine("TileImageLoader: {0}: {1} {2}", uri, (int)response.StatusCode, response.ReasonPhrase);
}
}
catch (Exception ex)
{
Debug.WriteLine("TileImageLoader: {0}: {1}", uri, ex.Message);
}
return false;
}
private async Task SetTileImageAsync(Tile tile, IBuffer buffer)
{
var tcs = new TaskCompletionSource<object>();
using (var stream = new InMemoryRandomAccessStream())
{
await stream.WriteAsync(buffer);
await stream.FlushAsync(); // necessary?
stream.Seek(0);
await tile.Image.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () =>
{
try
{
var bitmapImage = new BitmapImage();
await bitmapImage.SetSourceAsync(stream);
tile.SetImage(bitmapImage);
tcs.SetResult(null);
}
catch (Exception ex)
{
tcs.SetException(ex);
}
});
}
await tcs.Task;
}
}
}

View file

@ -0,0 +1,16 @@
{
"dependencies": {
"Microsoft.NETCore.UniversalWindowsPlatform": "5.1.0"
},
"frameworks": {
"uap10.0": {}
},
"runtimes": {
"win10-arm": {},
"win10-arm-aot": {},
"win10-x86": {},
"win10-x86-aot": {},
"win10-x64": {},
"win10-x64-aot": {}
}
}