Version 4.1.

This commit is contained in:
ClemensF 2017-09-05 20:57:17 +02:00
parent ef6d1ed959
commit 467d82ead7
25 changed files with 323 additions and 309 deletions

View file

@ -6,11 +6,14 @@ using Windows.Foundation;
using Windows.UI;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Media.Animation;
namespace MapControl
{
public partial class MapBase
{
private const FillBehavior AnimationFillBehavior = FillBehavior.HoldEnd;
public static readonly DependencyProperty ForegroundProperty = DependencyProperty.Register(
nameof(Foreground), typeof(Brush), typeof(MapBase),
new PropertyMetadata(new SolidColorBrush(Colors.Black)));
@ -39,25 +42,24 @@ namespace MapControl
nameof(TargetHeading), typeof(double), typeof(MapBase),
new PropertyMetadata(0d, (o, e) => ((MapBase)o).TargetHeadingPropertyChanged((double)e.NewValue)));
partial void Initialize()
public MapBase()
{
MapProjection = new WebMercatorProjection();
ScaleRotateTransform.Children.Add(ScaleTransform);
ScaleRotateTransform.Children.Add(RotateTransform);
// 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;
Clip = new RectangleGeometry();
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();
}
Clip.Rect = new Rect(0d, 0d, e.NewSize.Width, e.NewSize.Height);
ResetTransformCenter();
UpdateTransform();
};
}
}

View file

@ -27,6 +27,7 @@
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<GenerateLibraryLayout>true</GenerateLibraryLayout>
<NoWarn>CS1998</NoWarn>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
@ -88,6 +89,9 @@
<Compile Include="..\Shared\MapImageLayer.cs">
<Link>MapImageLayer.cs</Link>
</Compile>
<Compile Include="..\Shared\MapItemsControl.cs">
<Link>MapItemsControl.cs</Link>
</Compile>
<Compile Include="..\Shared\MapOverlay.cs">
<Link>MapOverlay.cs</Link>
</Compile>
@ -112,6 +116,9 @@
<Compile Include="..\Shared\OrthographicProjection.cs">
<Link>OrthographicProjection.cs</Link>
</Compile>
<Compile Include="..\Shared\Pushpin.cs">
<Link>Pushpin.cs</Link>
</Compile>
<Compile Include="..\Shared\StereographicProjection.cs">
<Link>StereographicProjection.cs</Link>
</Compile>
@ -143,18 +150,15 @@
<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" />
<Compile Include="TileSource.UWP.cs" />
<EmbeddedResource Include="Properties\MapControl.UWP.rd.xml" />
</ItemGroup>
<ItemGroup>

View file

@ -1,26 +0,0 @@
// 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

@ -1,31 +0,0 @@
// 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

@ -56,7 +56,7 @@ namespace MapControl
public static readonly DependencyProperty StrokeMiterLimitProperty = DependencyProperty.Register(
nameof(StrokeMiterLimit), typeof(double), typeof(MapOverlay), new PropertyMetadata(1d));
protected override void SetParentMapOverride(MapBase parentMap)
protected override void SetParentMap(MapBase parentMap)
{
if (GetBindingExpression(ForegroundProperty) != null)
{
@ -89,7 +89,7 @@ namespace MapControl
}
}
base.SetParentMapOverride(parentMap);
base.SetParentMap(parentMap);
}
}
}

View file

@ -12,27 +12,20 @@ namespace MapControl
public static readonly DependencyProperty ParentMapProperty = DependencyProperty.RegisterAttached(
"ParentMap", typeof(MapBase), typeof(MapPanel), new PropertyMetadata(null, ParentMapPropertyChanged));
public MapPanel()
public static void InitMapElement(FrameworkElement element)
{
if (this is MapBase)
if (element is MapBase)
{
SetValue(ParentMapProperty, this);
element.SetValue(ParentMapProperty, element);
}
else
{
AddParentMapHandlers(this);
}
}
// Workaround for missing property value inheritance in Windows Runtime.
// Loaded and Unloaded handlers set and clear the ParentMap property value.
/// <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);
element.Loaded += (s, e) => GetParentMap(element);
element.Unloaded += (s, e) => element.ClearValue(ParentMapProperty);
}
}
public static MapBase GetParentMap(UIElement element)
@ -49,20 +42,12 @@ namespace MapControl
private static MapBase FindParentMap(UIElement element)
{
MapBase parentMap = null;
var parentElement = VisualTreeHelper.GetParent(element) as UIElement;
var parent = VisualTreeHelper.GetParent(element) as UIElement;
if (parentElement != null)
{
parentMap = parentElement as MapBase;
if (parentMap == null)
{
parentMap = GetParentMap(parentElement);
}
}
return parentMap;
return parent == null ? null
: ((parent as MapBase)
?? (MapBase)element.GetValue(ParentMapProperty)
?? FindParentMap(parent));
}
}
}

View file

@ -12,11 +12,6 @@ namespace MapControl
{
private Geometry data;
public MapPath()
{
MapPanel.AddParentMapHandlers(this);
}
protected override Size MeasureOverride(Size availableSize)
{
if (Stretch != Stretch.None)

View file

@ -1,26 +0,0 @@
// 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

@ -9,7 +9,6 @@ using Windows.Storage;
using Windows.Storage.Streams;
using Windows.UI.Core;
using Windows.UI.Xaml.Media.Imaging;
using Windows.Web.Http;
namespace MapControl
{
@ -31,11 +30,6 @@ namespace MapControl
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);
@ -49,28 +43,26 @@ namespace MapControl
private async Task<bool> DownloadTileImageAsync(Tile tile, Uri uri, string cacheKey)
{
var success = false;
try
{
using (var response = await HttpClient.GetAsync(uri))
using (var response = await TileSource.HttpClient.GetAsync(uri))
{
if (response.IsSuccessStatusCode)
success = response.IsSuccessStatusCode;
if (!success)
{
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);
}
else if (TileSource.TileAvailable(response.Headers))
{
var buffer = await response.Content.ReadAsBufferAsync();
Debug.WriteLine("TileImageLoader: {0}: {1} {2}", uri, (int)response.StatusCode, response.ReasonPhrase);
await SetTileImageAsync(tile, buffer); // create BitmapImage before caching
await Cache.SetAsync(cacheKey, buffer, GetExpiration(response));
}
}
}
catch (Exception ex)
@ -78,7 +70,7 @@ namespace MapControl
Debug.WriteLine("TileImageLoader: {0}: {1}", uri, ex.Message);
}
return false;
return success;
}
private async Task SetTileImageAsync(Tile tile, IBuffer buffer)

View file

@ -0,0 +1,84 @@
// 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.Streams;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Media.Imaging;
using Windows.Web.Http;
using Windows.Web.Http.Headers;
namespace MapControl
{
public partial class TileSource
{
/// <summary>
/// The HttpClient instance used when image data is downloaded from a web resource.
/// </summary>
public static HttpClient HttpClient { get; set; } = new HttpClient();
/// <summary>
/// Check HTTP response headers for tile unavailability, e.g. X-VE-Tile-Info=no-tile
/// </summary>
public static bool TileAvailable(HttpResponseHeaderCollection responseHeaders)
{
string tileInfo;
return !responseHeaders.TryGetValue("X-VE-Tile-Info", out tileInfo) || tileInfo != "no-tile";
}
/// <summary>
/// Load a tile ImageSource asynchronously from GetUri(x, y, zoomLevel)
/// </summary>
public virtual async Task<ImageSource> LoadImageAsync(int x, int y, int zoomLevel)
{
ImageSource imageSource = null;
var uri = GetUri(x, y, zoomLevel);
if (uri != null)
{
try
{
if (uri.Scheme == "http")
{
using (var response = await HttpClient.GetAsync(uri))
{
if (!response.IsSuccessStatusCode)
{
Debug.WriteLine("TileSource: {0}: {1} {2}", uri, (int)response.StatusCode, response.ReasonPhrase);
}
else if (TileAvailable(response.Headers))
{
var bitmapImage = new BitmapImage();
using (var stream = new InMemoryRandomAccessStream())
{
await response.Content.WriteToStreamAsync(stream);
stream.Seek(0);
await bitmapImage.SetSourceAsync(stream);
}
imageSource = bitmapImage;
}
}
}
else
{
imageSource = new BitmapImage(uri);
}
}
catch (Exception ex)
{
Debug.WriteLine("TileSource: {0}: {1}", uri, ex.Message);
}
}
return imageSource;
}
}
}