mirror of
https://github.com/ClemensFischer/XAML-Map-Control.git
synced 2026-04-05 14:37:01 +00:00
Version 4: Upgrade to VS 2017
This commit is contained in:
parent
2aafe32e00
commit
ec47f225b3
142 changed files with 1828 additions and 18384 deletions
|
|
@ -1,179 +0,0 @@
|
|||
// 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.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Net;
|
||||
using System.Xml;
|
||||
#if NETFX_CORE
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Media;
|
||||
using Windows.UI.Xaml.Media.Imaging;
|
||||
#else
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
#endif
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
/// <summary>
|
||||
/// Displays Bing Maps tiles. The static ApiKey property must be set to a Bing Maps API Key.
|
||||
/// Tile image URLs and min/max zoom levels are retrieved from the Imagery Metadata Service
|
||||
/// (see http://msdn.microsoft.com/en-us/library/ff701716.aspx).
|
||||
/// </summary>
|
||||
public class BingMapsTileLayer : MapTileLayer
|
||||
{
|
||||
public enum MapMode
|
||||
{
|
||||
Road, Aerial, AerialWithLabels
|
||||
}
|
||||
|
||||
public BingMapsTileLayer()
|
||||
: this(new TileImageLoader())
|
||||
{
|
||||
}
|
||||
|
||||
public BingMapsTileLayer(ITileImageLoader tileImageLoader)
|
||||
: base(tileImageLoader)
|
||||
{
|
||||
MinZoomLevel = 1;
|
||||
MaxZoomLevel = 21;
|
||||
Loaded += OnLoaded;
|
||||
}
|
||||
|
||||
public static string ApiKey { get; set; }
|
||||
|
||||
public MapMode Mode { get; set; }
|
||||
public string Culture { get; set; }
|
||||
public ImageSource LogoImage { get; set; }
|
||||
|
||||
private void OnLoaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Loaded -= OnLoaded;
|
||||
|
||||
if (string.IsNullOrEmpty(ApiKey))
|
||||
{
|
||||
Debug.WriteLine("BingMapsTileLayer requires a Bing Maps API Key");
|
||||
}
|
||||
else
|
||||
{
|
||||
var uri = string.Format("http://dev.virtualearth.net/REST/V1/Imagery/Metadata/{0}?output=xml&key={1}", Mode, ApiKey);
|
||||
var request = WebRequest.CreateHttp(uri);
|
||||
|
||||
request.BeginGetResponse(HandleImageryMetadataResponse, request);
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleImageryMetadataResponse(IAsyncResult asyncResult)
|
||||
{
|
||||
try
|
||||
{
|
||||
var request = (HttpWebRequest)asyncResult.AsyncState;
|
||||
|
||||
using (var response = request.EndGetResponse(asyncResult))
|
||||
using (var xmlReader = XmlReader.Create(response.GetResponseStream()))
|
||||
{
|
||||
ReadImageryMetadataResponse(xmlReader);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine(ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
private void ReadImageryMetadataResponse(XmlReader xmlReader)
|
||||
{
|
||||
string logoUri = null;
|
||||
string imageUrl = null;
|
||||
string[] imageUrlSubdomains = null;
|
||||
int? zoomMin = null;
|
||||
int? zoomMax = null;
|
||||
|
||||
do
|
||||
{
|
||||
if (xmlReader.NodeType == XmlNodeType.Element)
|
||||
{
|
||||
switch (xmlReader.Name)
|
||||
{
|
||||
case "BrandLogoUri":
|
||||
logoUri = xmlReader.ReadElementContentAsString();
|
||||
break;
|
||||
case "ImageUrl":
|
||||
imageUrl = xmlReader.ReadElementContentAsString();
|
||||
break;
|
||||
case "ImageUrlSubdomains":
|
||||
imageUrlSubdomains = ReadStrings(xmlReader.ReadSubtree());
|
||||
break;
|
||||
case "ZoomMin":
|
||||
zoomMin = xmlReader.ReadElementContentAsInt();
|
||||
break;
|
||||
case "ZoomMax":
|
||||
zoomMax = xmlReader.ReadElementContentAsInt();
|
||||
break;
|
||||
default:
|
||||
xmlReader.Read();
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
xmlReader.Read();
|
||||
}
|
||||
}
|
||||
while (xmlReader.NodeType != XmlNodeType.None);
|
||||
|
||||
if (!string.IsNullOrEmpty(imageUrl) && imageUrlSubdomains != null && imageUrlSubdomains.Length > 0)
|
||||
{
|
||||
var op = Dispatcher.BeginInvoke(new Action(() =>
|
||||
{
|
||||
if (string.IsNullOrEmpty(Culture))
|
||||
{
|
||||
Culture = CultureInfo.CurrentUICulture.Name;
|
||||
}
|
||||
|
||||
TileSource = new BingMapsTileSource(imageUrl.Replace("{culture}", Culture), imageUrlSubdomains);
|
||||
|
||||
if (zoomMin.HasValue && zoomMin.Value > MinZoomLevel)
|
||||
{
|
||||
MinZoomLevel = zoomMin.Value;
|
||||
}
|
||||
|
||||
if (zoomMax.HasValue && zoomMax.Value < MaxZoomLevel)
|
||||
{
|
||||
MaxZoomLevel = zoomMax.Value;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(logoUri))
|
||||
{
|
||||
LogoImage = new BitmapImage(new Uri(logoUri));
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
private static string[] ReadStrings(XmlReader xmlReader)
|
||||
{
|
||||
var strings = new List<string>();
|
||||
|
||||
do
|
||||
{
|
||||
if (xmlReader.NodeType == XmlNodeType.Element && xmlReader.Name == "string")
|
||||
{
|
||||
strings.Add(xmlReader.ReadElementContentAsString());
|
||||
}
|
||||
else
|
||||
{
|
||||
xmlReader.Read();
|
||||
}
|
||||
}
|
||||
while (xmlReader.NodeType != XmlNodeType.None);
|
||||
|
||||
return strings.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
// 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.IO;
|
||||
using System.Net;
|
||||
using System.Windows.Media.Imaging;
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates frozen BitmapSources from Stream, file or Uri.
|
||||
/// </summary>
|
||||
public static class BitmapSourceHelper
|
||||
{
|
||||
public static BitmapSource FromStream(Stream stream)
|
||||
{
|
||||
return BitmapFrame.Create(stream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
|
||||
}
|
||||
|
||||
public static BitmapSource FromFile(string path)
|
||||
{
|
||||
if (!File.Exists(path))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
using (var fileStream = new FileStream(path, FileMode.Open, FileAccess.Read))
|
||||
{
|
||||
return FromStream(fileStream);
|
||||
}
|
||||
}
|
||||
|
||||
public static BitmapSource FromUri(Uri uri)
|
||||
{
|
||||
if (!uri.IsAbsoluteUri)
|
||||
{
|
||||
return FromFile(uri.OriginalString);
|
||||
}
|
||||
|
||||
if (uri.Scheme == "file")
|
||||
{
|
||||
return FromFile(uri.LocalPath);
|
||||
}
|
||||
|
||||
using (var response = WebRequest.Create(uri).GetResponse())
|
||||
using (var responseStream = response.GetResponseStream())
|
||||
using (var memoryStream = new MemoryStream())
|
||||
{
|
||||
responseStream.CopyTo(memoryStream);
|
||||
memoryStream.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
return FromStream(memoryStream);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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 System;
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
public class BoundingBoxConverter : TypeConverter
|
||||
{
|
||||
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
|
||||
{
|
||||
return sourceType == typeof(string);
|
||||
}
|
||||
|
||||
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
|
||||
{
|
||||
return BoundingBox.Parse((string)value);
|
||||
}
|
||||
}
|
||||
|
||||
[TypeConverter(typeof(BoundingBoxConverter))]
|
||||
#if !SILVERLIGHT
|
||||
[Serializable]
|
||||
#endif
|
||||
public partial class BoundingBox
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
|
||||
// © 2017 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using System.Windows;
|
||||
using System.Windows.Media.Animation;
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
internal static class Extensions
|
||||
{
|
||||
public static void BeginAnimation(this DependencyObject obj, DependencyProperty property, Timeline animation)
|
||||
{
|
||||
Storyboard.SetTargetProperty(animation, new PropertyPath(property));
|
||||
Storyboard.SetTarget(animation, obj);
|
||||
var storyboard = new Storyboard();
|
||||
storyboard.Children.Add(animation);
|
||||
storyboard.Begin();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
|
||||
// © 2017 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
#if NETFX_CORE
|
||||
using Windows.UI.Xaml.Media;
|
||||
using Windows.UI.Xaml.Media.Imaging;
|
||||
#else
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
#endif
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public class ImageTileSource : TileSource
|
||||
{
|
||||
public virtual ImageSource LoadImage(int x, int y, int zoomLevel)
|
||||
{
|
||||
var uri = GetUri(x, y, zoomLevel);
|
||||
|
||||
return uri != null ? new BitmapImage(uri) : null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
|
||||
// © 2017 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// LoadImage will be called from a non-UI thread and must therefore return a frozen ImageSource.
|
||||
/// </summary>
|
||||
public class ImageTileSource : TileSource
|
||||
{
|
||||
public virtual ImageSource LoadImage(int x, int y, int zoomLevel)
|
||||
{
|
||||
var uri = GetUri(x, y, zoomLevel);
|
||||
|
||||
return uri != null ? BitmapSourceHelper.FromUri(uri) : null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
// 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.ComponentModel;
|
||||
using System.Globalization;
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
public class LocationCollectionConverter : TypeConverter
|
||||
{
|
||||
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
|
||||
{
|
||||
return sourceType == typeof(string);
|
||||
}
|
||||
|
||||
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
|
||||
{
|
||||
return LocationCollection.Parse((string)value);
|
||||
}
|
||||
}
|
||||
|
||||
[TypeConverter(typeof(LocationCollectionConverter))]
|
||||
public partial class LocationCollection
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -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 System;
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
public class LocationConverter : TypeConverter
|
||||
{
|
||||
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
|
||||
{
|
||||
return sourceType == typeof(string);
|
||||
}
|
||||
|
||||
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
|
||||
{
|
||||
return Location.Parse((string)value);
|
||||
}
|
||||
}
|
||||
|
||||
[TypeConverter(typeof(LocationConverter))]
|
||||
#if !SILVERLIGHT
|
||||
[Serializable]
|
||||
#endif
|
||||
public partial class Location
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -1,70 +0,0 @@
|
|||
// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
|
||||
// © 2017 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using System.Windows;
|
||||
using System.Windows.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 Point? mousePosition;
|
||||
|
||||
public Map()
|
||||
{
|
||||
MouseWheel += OnMouseWheel;
|
||||
MouseLeftButtonDown += OnMouseLeftButtonDown;
|
||||
MouseLeftButtonUp += OnMouseLeftButtonUp;
|
||||
MouseMove += OnMouseMove;
|
||||
}
|
||||
|
||||
/// <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); }
|
||||
}
|
||||
|
||||
private void OnMouseWheel(object sender, MouseWheelEventArgs e)
|
||||
{
|
||||
var zoomChange = MouseWheelZoomDelta * e.Delta / 120d;
|
||||
ZoomMap(e.GetPosition(this), TargetZoomLevel + zoomChange);
|
||||
}
|
||||
|
||||
private void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
if (CaptureMouse())
|
||||
{
|
||||
mousePosition = e.GetPosition(this);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
if (mousePosition.HasValue)
|
||||
{
|
||||
mousePosition = null;
|
||||
ReleaseMouseCapture();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnMouseMove(object sender, MouseEventArgs e)
|
||||
{
|
||||
if (mousePosition.HasValue)
|
||||
{
|
||||
var position = e.GetPosition(this);
|
||||
TranslateMap(new Point(position.X - mousePosition.Value.X, position.Y - mousePosition.Value.Y));
|
||||
mousePosition = position;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,156 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProductVersion>8.0.50727</ProductVersion>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<ProjectGuid>{EB133B78-DEFF-416A-8F0C-89E54D766576}</ProjectGuid>
|
||||
<ProjectTypeGuids>{A1591282-1198-4647-A2B1-27E5FF5F6F3B};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>MapControl</RootNamespace>
|
||||
<AssemblyName>MapControl.Silverlight</AssemblyName>
|
||||
<TargetFrameworkIdentifier>Silverlight</TargetFrameworkIdentifier>
|
||||
<TargetFrameworkVersion>v5.0</TargetFrameworkVersion>
|
||||
<SilverlightVersion>$(TargetFrameworkVersion)</SilverlightVersion>
|
||||
<SilverlightApplication>false</SilverlightApplication>
|
||||
<ValidateXaml>true</ValidateXaml>
|
||||
<ThrowErrorsInValidation>true</ThrowErrorsInValidation>
|
||||
<TargetFrameworkProfile />
|
||||
</PropertyGroup>
|
||||
<!-- This property group is only here to support building this project using the
|
||||
MSBuild 3.5 toolset. In order to work correctly with this older toolset, it needs
|
||||
to set the TargetFrameworkVersion to v3.5 -->
|
||||
<PropertyGroup Condition="'$(MSBuildToolsVersion)' == '3.5'">
|
||||
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE;SILVERLIGHT</DefineConstants>
|
||||
<NoStdLib>true</NoStdLib>
|
||||
<NoConfig>true</NoConfig>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>none</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE;SILVERLIGHT</DefineConstants>
|
||||
<NoStdLib>true</NoStdLib>
|
||||
<NoConfig>true</NoConfig>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<RunPostBuildEvent>OnBuildSuccess</RunPostBuildEvent>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<SignAssembly>true</SignAssembly>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<AssemblyOriginatorKeyFile>..\MapControl.snk</AssemblyOriginatorKeyFile>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="mscorlib" />
|
||||
<Reference Include="System.Windows" />
|
||||
<Reference Include="system" />
|
||||
<Reference Include="System.Core">
|
||||
<HintPath>$(TargetFrameworkDirectory)System.Core.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Net" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="AzimuthalEquidistantProjection.cs" />
|
||||
<Compile Include="AzimuthalProjection.cs" />
|
||||
<Compile Include="BingMapsTileLayer.cs" />
|
||||
<Compile Include="BingMapsTileSource.cs" />
|
||||
<Compile Include="BoundingBox.cs" />
|
||||
<Compile Include="BoundingBoxConverter.cs" />
|
||||
<Compile Include="CenteredBoundingBox.cs" />
|
||||
<Compile Include="EquirectangularProjection.cs" />
|
||||
<Compile Include="Extensions.Silverlight.cs" />
|
||||
<Compile Include="GnomonicProjection.cs" />
|
||||
<Compile Include="HyperlinkText.cs" />
|
||||
<Compile Include="ImageTileSource.Silverlight.WinRT.cs" />
|
||||
<Compile Include="Location.cs" />
|
||||
<Compile Include="LocationCollection.cs" />
|
||||
<Compile Include="LocationCollectionConverter.cs" />
|
||||
<Compile Include="LocationConverter.cs" />
|
||||
<Compile Include="Map.Silverlight.cs" />
|
||||
<Compile Include="MapBase.cs" />
|
||||
<Compile Include="MapBase.Silverlight.WinRT.cs" />
|
||||
<Compile Include="MapImageLayer.cs" />
|
||||
<Compile Include="MapImageLayer.Silverlight.WinRT.cs" />
|
||||
<Compile Include="MapGraticule.cs" />
|
||||
<Compile Include="MapGraticule.Silverlight.WinRT.cs" />
|
||||
<Compile Include="MapItem.Silverlight.WinRT.cs" />
|
||||
<Compile Include="MapItemsControl.Silverlight.WinRT.cs" />
|
||||
<Compile Include="MapOverlay.cs" />
|
||||
<Compile Include="MapOverlay.Silverlight.WinRT.cs" />
|
||||
<Compile Include="MapPanel.cs" />
|
||||
<Compile Include="MapPanel.Silverlight.WinRT.cs" />
|
||||
<Compile Include="MapPath.cs" />
|
||||
<Compile Include="MapPath.Silverlight.WinRT.cs" />
|
||||
<Compile Include="MapPolyline.cs" />
|
||||
<Compile Include="MapPolyline.Silverlight.WinRT.cs" />
|
||||
<Compile Include="MapProjection.cs" />
|
||||
<Compile Include="MapTileLayer.cs" />
|
||||
<Compile Include="MatrixEx.Silverlight.WinRT.cs" />
|
||||
<Compile Include="OrthographicProjection.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Pushpin.Silverlight.WinRT.cs" />
|
||||
<Compile Include="StereographicProjection.cs" />
|
||||
<Compile Include="Tile.cs" />
|
||||
<Compile Include="Tile.Silverlight.WinRT.cs" />
|
||||
<Compile Include="TileGrid.cs" />
|
||||
<Compile Include="TileImageLoader.Silverlight.cs" />
|
||||
<Compile Include="MapTileLayer.Silverlight.WinRT.cs" />
|
||||
<Compile Include="TileSource.cs" />
|
||||
<Compile Include="TileSourceConverter.cs" />
|
||||
<Compile Include="ViewportChangedEventArgs.cs" />
|
||||
<Compile Include="WebMercatorProjection.cs" />
|
||||
<Compile Include="WmsImageLayer.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Include="Themes\Generic.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="..\MapControl.snk">
|
||||
<Link>MapControl.snk</Link>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\Silverlight\$(SilverlightVersion)\Microsoft.Silverlight.CSharp.targets" />
|
||||
<ProjectExtensions>
|
||||
<VisualStudio>
|
||||
<FlavorProperties GUID="{A1591282-1198-4647-A2B1-27E5FF5F6F3B}">
|
||||
<SilverlightProjectProperties />
|
||||
</FlavorProperties>
|
||||
</VisualStudio>
|
||||
</ProjectExtensions>
|
||||
<PropertyGroup>
|
||||
<PreBuildEvent>
|
||||
</PreBuildEvent>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<PostBuildEvent>
|
||||
</PostBuildEvent>
|
||||
</PropertyGroup>
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ProjectExtensions>
|
||||
<VisualStudio>
|
||||
<FlavorProperties GUID="{A1591282-1198-4647-A2B1-27E5FF5F6F3B}">
|
||||
<SilverlightProjectProperties>
|
||||
<StartPageUrl>
|
||||
</StartPageUrl>
|
||||
<StartAction>DynamicPage</StartAction>
|
||||
<AspNetDebugging>True</AspNetDebugging>
|
||||
<NativeDebugging>False</NativeDebugging>
|
||||
<SQLDebugging>False</SQLDebugging>
|
||||
<ExternalProgram>
|
||||
</ExternalProgram>
|
||||
<StartExternalURL>
|
||||
</StartExternalURL>
|
||||
<StartCmdLineArguments>
|
||||
</StartCmdLineArguments>
|
||||
<StartWorkingDirectory>
|
||||
</StartWorkingDirectory>
|
||||
<ShowWebRefOnDebugPrompt>True</ShowWebRefOnDebugPrompt>
|
||||
<OutOfBrowserProjectToDebug>
|
||||
</OutOfBrowserProjectToDebug>
|
||||
<ShowRiaSvcsOnDebugPrompt>True</ShowRiaSvcsOnDebugPrompt>
|
||||
</SilverlightProjectProperties>
|
||||
</FlavorProperties>
|
||||
</VisualStudio>
|
||||
</ProjectExtensions>
|
||||
</Project>
|
||||
|
|
@ -1,135 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="12.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>{226F3575-B683-446D-A2F0-181291DC8787}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>MapControl</RootNamespace>
|
||||
<AssemblyName>MapControl.WPF</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<TargetFrameworkProfile />
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>TRACE;DEBUG</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>none</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<RunPostBuildEvent>OnBuildSuccess</RunPostBuildEvent>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<SignAssembly>true</SignAssembly>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<AssemblyOriginatorKeyFile>..\MapControl.snk</AssemblyOriginatorKeyFile>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="PresentationCore" />
|
||||
<Reference Include="PresentationFramework" />
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Runtime.Caching" />
|
||||
<Reference Include="System.Xaml" />
|
||||
<Reference Include="System.XML" />
|
||||
<Reference Include="WindowsBase" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="AzimuthalEquidistantProjection.cs" />
|
||||
<Compile Include="AzimuthalProjection.cs" />
|
||||
<Compile Include="BingMapsTileLayer.cs" />
|
||||
<Compile Include="BingMapsTileSource.cs" />
|
||||
<Compile Include="BoundingBox.cs" />
|
||||
<Compile Include="BoundingBoxConverter.cs" />
|
||||
<Compile Include="CenteredBoundingBox.cs" />
|
||||
<Compile Include="EquirectangularProjection.cs" />
|
||||
<Compile Include="GnomonicProjection.cs" />
|
||||
<Compile Include="BitmapSourceHelper.cs" />
|
||||
<Compile Include="HyperlinkText.cs" />
|
||||
<Compile Include="ImageTileSource.WPF.cs" />
|
||||
<Compile Include="Location.cs" />
|
||||
<Compile Include="LocationCollection.cs" />
|
||||
<Compile Include="LocationCollectionConverter.cs" />
|
||||
<Compile Include="LocationConverter.cs" />
|
||||
<Compile Include="Map.WPF.cs" />
|
||||
<Compile Include="MapBase.cs" />
|
||||
<Compile Include="MapBase.WPF.cs" />
|
||||
<Compile Include="MapImageLayer.cs" />
|
||||
<Compile Include="MapImageLayer.WPF.cs" />
|
||||
<Compile Include="MapGraticule.cs" />
|
||||
<Compile Include="MapGraticule.WPF.cs" />
|
||||
<Compile Include="MapItem.WPF.cs" />
|
||||
<Compile Include="MapItemsControl.WPF.cs" />
|
||||
<Compile Include="MapOverlay.cs" />
|
||||
<Compile Include="MapOverlay.WPF.cs" />
|
||||
<Compile Include="MapPanel.cs" />
|
||||
<Compile Include="MapPanel.WPF.cs" />
|
||||
<Compile Include="MapPath.cs" />
|
||||
<Compile Include="MapPath.WPF.cs" />
|
||||
<Compile Include="MapPolyline.cs" />
|
||||
<Compile Include="MapPolyline.WPF.cs" />
|
||||
<Compile Include="MapScale.cs" />
|
||||
<Compile Include="MapProjection.cs" />
|
||||
<Compile Include="MatrixEx.WPF.cs" />
|
||||
<Compile Include="OrthographicProjection.cs" />
|
||||
<Compile Include="StereographicProjection.cs" />
|
||||
<Compile Include="WebMercatorProjection.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Pushpin.WPF.cs" />
|
||||
<Compile Include="Tile.cs" />
|
||||
<Compile Include="Tile.WPF.cs" />
|
||||
<Compile Include="TileGrid.cs" />
|
||||
<Compile Include="TileImageLoader.WPF.cs" />
|
||||
<Compile Include="MapTileLayer.cs" />
|
||||
<Compile Include="MapTileLayer.WPF.cs" />
|
||||
<Compile Include="TileSource.cs" />
|
||||
<Compile Include="TileSourceConverter.cs" />
|
||||
<Compile Include="ViewportChangedEventArgs.cs" />
|
||||
<Compile Include="WmsImageLayer.cs" />
|
||||
<Compile Include="WmsLayers.WPF.WinRT.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Include="Themes\Generic.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="..\MapControl.snk">
|
||||
<Link>MapControl.snk</Link>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<PropertyGroup>
|
||||
<PreBuildEvent>
|
||||
</PreBuildEvent>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<PostBuildEvent>
|
||||
</PostBuildEvent>
|
||||
</PropertyGroup>
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
||||
|
|
@ -1,69 +0,0 @@
|
|||
// 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 System.Windows.Controls;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
public partial class MapImageLayer
|
||||
{
|
||||
protected void UpdateImage(Uri uri)
|
||||
{
|
||||
Task.Run(() =>
|
||||
{
|
||||
BitmapSource image = null;
|
||||
|
||||
try
|
||||
{
|
||||
image = BitmapSourceHelper.FromUri(uri);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine("{0}: {1}", uri, ex.Message);
|
||||
}
|
||||
|
||||
Dispatcher.BeginInvoke(new Action(() => UpdateImage(image)));
|
||||
});
|
||||
}
|
||||
|
||||
protected void UpdateImage(BitmapSource bitmap)
|
||||
{
|
||||
SetTopImage(bitmap);
|
||||
|
||||
if (bitmap != null && !bitmap.IsFrozen && bitmap.IsDownloading)
|
||||
{
|
||||
bitmap.DownloadCompleted += BitmapDownloadCompleted;
|
||||
bitmap.DownloadFailed += BitmapDownloadFailed;
|
||||
}
|
||||
else
|
||||
{
|
||||
SwapImages();
|
||||
}
|
||||
}
|
||||
|
||||
private void BitmapDownloadCompleted(object sender, EventArgs e)
|
||||
{
|
||||
var bitmap = (BitmapSource)sender;
|
||||
bitmap.DownloadCompleted -= BitmapDownloadCompleted;
|
||||
bitmap.DownloadFailed -= BitmapDownloadFailed;
|
||||
|
||||
SwapImages();
|
||||
}
|
||||
|
||||
private void BitmapDownloadFailed(object sender, ExceptionEventArgs e)
|
||||
{
|
||||
var bitmap = (BitmapSource)sender;
|
||||
bitmap.DownloadCompleted -= BitmapDownloadCompleted;
|
||||
bitmap.DownloadFailed -= BitmapDownloadFailed;
|
||||
|
||||
((Image)Children[topImageIndex]).Source = null;
|
||||
SwapImages();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,82 +0,0 @@
|
|||
// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
|
||||
// © 2017 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using System;
|
||||
#if NETFX_CORE
|
||||
using Windows.Foundation;
|
||||
using Windows.UI.Xaml.Media;
|
||||
#else
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
#endif
|
||||
|
||||
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)
|
||||
{
|
||||
return new Matrix(1d, 0d, 0d, 1d, -translation1X, -translation1Y)
|
||||
.Scale(scaleX, scaleY)
|
||||
.Rotate(rotationAngle)
|
||||
.Translate(translation2X, translation2Y);
|
||||
}
|
||||
|
||||
private static Matrix Translate(this Matrix matrix, double offsetX, double offsetY)
|
||||
{
|
||||
matrix.OffsetX += offsetX;
|
||||
matrix.OffsetY += offsetY;
|
||||
return matrix;
|
||||
}
|
||||
|
||||
private static Matrix Scale(this Matrix matrix, double scaleX, double scaleY)
|
||||
{
|
||||
return Multiply(matrix, new Matrix(scaleX, 0d, 0d, scaleY, 0d, 0d));
|
||||
}
|
||||
|
||||
private static Matrix Rotate(this Matrix matrix, double angle)
|
||||
{
|
||||
if (angle == 0d)
|
||||
{
|
||||
return matrix;
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
private static Matrix Invert(this Matrix matrix)
|
||||
{
|
||||
var determinant = matrix.M11 * matrix.M22 - matrix.M12 * matrix.M21;
|
||||
|
||||
return new Matrix(
|
||||
matrix.M22 / determinant,
|
||||
-matrix.M12 / determinant,
|
||||
-matrix.M21 / determinant,
|
||||
matrix.M11 / determinant,
|
||||
(matrix.M21 * matrix.OffsetY - matrix.M22 * matrix.OffsetX) / determinant,
|
||||
(matrix.M12 * matrix.OffsetX - matrix.M11 * matrix.OffsetY) / determinant);
|
||||
}
|
||||
|
||||
private static Matrix Multiply(this Matrix matrix1, Matrix matrix2)
|
||||
{
|
||||
return new Matrix(
|
||||
matrix1.M11 * matrix2.M11 + matrix1.M12 * matrix2.M21,
|
||||
matrix1.M11 * matrix2.M12 + matrix1.M12 * matrix2.M22,
|
||||
matrix1.M21 * matrix2.M11 + matrix1.M22 * matrix2.M21,
|
||||
matrix1.M21 * matrix2.M12 + matrix1.M22 * matrix2.M22,
|
||||
(matrix2.M11 * matrix1.OffsetX + matrix2.M21 * matrix1.OffsetY) + matrix2.OffsetX,
|
||||
(matrix2.M12 * matrix1.OffsetX + matrix2.M22 * matrix1.OffsetY) + matrix2.OffsetY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using System;
|
||||
#if NETFX_CORE
|
||||
#if WINDOWS_UWP
|
||||
using Windows.Foundation;
|
||||
#else
|
||||
using System.Windows;
|
||||
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
#if NETFX_CORE
|
||||
#if WINDOWS_UWP
|
||||
using Windows.Foundation;
|
||||
#else
|
||||
using System.Windows;
|
||||
|
|
@ -27,7 +27,7 @@ namespace MapControl
|
|||
|
||||
public override double GetViewportScale(double zoomLevel)
|
||||
{
|
||||
return base.GetViewportScale(zoomLevel) / MetersPerDegree;
|
||||
return DegreesToViewportScale(zoomLevel) / MetersPerDegree;
|
||||
}
|
||||
|
||||
public override Point GetMapScale(Location location)
|
||||
138
MapControl/Shared/BingMapsTileLayer.cs
Normal file
138
MapControl/Shared/BingMapsTileLayer.cs
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
// 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.Globalization;
|
||||
using System.Linq;
|
||||
#if WINDOWS_UWP
|
||||
using Windows.Data.Xml.Dom;
|
||||
using Windows.UI.Xaml;
|
||||
#else
|
||||
using System.Windows;
|
||||
using System.Xml;
|
||||
#endif
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
/// <summary>
|
||||
/// Displays Bing Maps tiles. The static ApiKey property must be set to a Bing Maps API Key.
|
||||
/// Tile image URLs and min/max zoom levels are retrieved from the Imagery Metadata Service
|
||||
/// (see http://msdn.microsoft.com/en-us/library/ff701716.aspx).
|
||||
/// </summary>
|
||||
public class BingMapsTileLayer : MapTileLayer
|
||||
{
|
||||
public enum MapMode
|
||||
{
|
||||
Road, Aerial, AerialWithLabels
|
||||
}
|
||||
|
||||
public BingMapsTileLayer()
|
||||
: this(new TileImageLoader())
|
||||
{
|
||||
}
|
||||
|
||||
public BingMapsTileLayer(ITileImageLoader tileImageLoader)
|
||||
: base(tileImageLoader)
|
||||
{
|
||||
MinZoomLevel = 1;
|
||||
MaxZoomLevel = 21;
|
||||
Loaded += OnLoaded;
|
||||
}
|
||||
|
||||
public static string ApiKey { get; set; }
|
||||
|
||||
public MapMode Mode { get; set; }
|
||||
public string Culture { get; set; }
|
||||
public Uri LogoImageUri { get; set; }
|
||||
|
||||
private async void OnLoaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Loaded -= OnLoaded;
|
||||
|
||||
if (string.IsNullOrEmpty(ApiKey))
|
||||
{
|
||||
Debug.WriteLine("BingMapsTileLayer requires a Bing Maps API Key");
|
||||
return;
|
||||
}
|
||||
|
||||
var imageryMetadataUrl = "http://dev.virtualearth.net/REST/V1/Imagery/Metadata/" + Mode;
|
||||
|
||||
try
|
||||
{
|
||||
var document = await XmlDocument.LoadFromUriAsync(new Uri(imageryMetadataUrl + "?output=xml&key=" + ApiKey));
|
||||
var imageryMetadata = document.DocumentElement.GetElementsByTagName("ImageryMetadata").OfType<XmlElement>().FirstOrDefault();
|
||||
|
||||
if (imageryMetadata != null)
|
||||
{
|
||||
ReadImageryMetadata(imageryMetadata);
|
||||
}
|
||||
|
||||
var brandLogoUri = document.DocumentElement.GetElementsByTagName("BrandLogoUri").OfType<XmlElement>().FirstOrDefault();
|
||||
|
||||
if (brandLogoUri != null)
|
||||
{
|
||||
LogoImageUri = new Uri(brandLogoUri.InnerText);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine("BingMapsTileLayer: {0}: {1}", imageryMetadataUrl, ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
private void ReadImageryMetadata(XmlElement imageryMetadata)
|
||||
{
|
||||
string imageUrl = null;
|
||||
string[] imageUrlSubdomains = null;
|
||||
int? zoomMin = null;
|
||||
int? zoomMax = null;
|
||||
|
||||
foreach (var element in imageryMetadata.ChildNodes.OfType<XmlElement>())
|
||||
{
|
||||
switch ((string)element.LocalName)
|
||||
{
|
||||
case "ImageUrl":
|
||||
imageUrl = element.InnerText;
|
||||
break;
|
||||
case "ImageUrlSubdomains":
|
||||
imageUrlSubdomains = element.ChildNodes
|
||||
.OfType<XmlElement>()
|
||||
.Where(e => (string)e.LocalName == "string")
|
||||
.Select(e => e.InnerText)
|
||||
.ToArray();
|
||||
break;
|
||||
case "ZoomMin":
|
||||
zoomMin = int.Parse(element.InnerText);
|
||||
break;
|
||||
case "ZoomMax":
|
||||
zoomMax = int.Parse(element.InnerText);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(imageUrl) && imageUrlSubdomains != null && imageUrlSubdomains.Length > 0)
|
||||
{
|
||||
if (zoomMin.HasValue && zoomMin.Value > MinZoomLevel)
|
||||
{
|
||||
MinZoomLevel = zoomMin.Value;
|
||||
}
|
||||
|
||||
if (zoomMax.HasValue && zoomMax.Value < MaxZoomLevel)
|
||||
{
|
||||
MaxZoomLevel = zoomMax.Value;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(Culture))
|
||||
{
|
||||
Culture = CultureInfo.CurrentUICulture.Name;
|
||||
}
|
||||
|
||||
TileSource = new BingMapsTileSource(imageUrl.Replace("{culture}", Culture), imageUrlSubdomains);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -6,7 +6,7 @@ using System;
|
|||
|
||||
namespace MapControl
|
||||
{
|
||||
internal class BingMapsTileSource : TileSource
|
||||
public class BingMapsTileSource : TileSource
|
||||
{
|
||||
private readonly string[] subdomains;
|
||||
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using System;
|
||||
#if NETFX_CORE
|
||||
#if WINDOWS_UWP
|
||||
using Windows.Foundation;
|
||||
#else
|
||||
using System.Windows;
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using System;
|
||||
#if NETFX_CORE
|
||||
#if WINDOWS_UWP
|
||||
using Windows.Foundation;
|
||||
#else
|
||||
using System.Windows;
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.RegularExpressions;
|
||||
#if NETFX_CORE
|
||||
#if WINDOWS_UWP
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Documents;
|
||||
|
|
@ -43,9 +43,7 @@ namespace MapControl
|
|||
|
||||
var link = new Hyperlink { NavigateUri = uri };
|
||||
link.Inlines.Add(new Run { Text = match.Groups[1].Value });
|
||||
#if SILVERLIGHT
|
||||
link.TargetName = "_blank";
|
||||
#elif !NETFX_CORE
|
||||
#if !WINDOWS_UWP
|
||||
link.ToolTip = uri.ToString();
|
||||
link.RequestNavigate += (s, e) => System.Diagnostics.Process.Start(e.Uri.ToString());
|
||||
#endif
|
||||
|
|
@ -86,16 +84,7 @@ namespace MapControl
|
|||
{
|
||||
inlines = ((Paragraph)obj).Inlines;
|
||||
}
|
||||
#if NETFX_CORE || SILVERLIGHT
|
||||
else if (obj is RichTextBlock)
|
||||
{
|
||||
var paragraph = new Paragraph();
|
||||
inlines = paragraph.Inlines;
|
||||
var richTextBlock = (RichTextBlock)obj;
|
||||
richTextBlock.Blocks.Clear();
|
||||
richTextBlock.Blocks.Add(paragraph);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (inlines != null)
|
||||
{
|
||||
inlines.Clear();
|
||||
|
|
@ -39,10 +39,9 @@ namespace MapControl
|
|||
|
||||
public bool Equals(Location location)
|
||||
{
|
||||
return ReferenceEquals(this, location)
|
||||
|| (location != null
|
||||
&& location.latitude == latitude
|
||||
&& location.longitude == longitude);
|
||||
return location != null
|
||||
&& Math.Abs(location.latitude - latitude) < 1e-9
|
||||
&& Math.Abs(location.longitude - longitude) < 1e-9;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using System;
|
||||
#if NETFX_CORE
|
||||
#if WINDOWS_UWP
|
||||
using Windows.Foundation;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Media;
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using System;
|
||||
#if NETFX_CORE
|
||||
#if WINDOWS_UWP
|
||||
using Windows.UI.Xaml;
|
||||
#else
|
||||
using System.Windows;
|
||||
|
|
@ -30,7 +30,7 @@ namespace MapControl
|
|||
|
||||
private double GetLineDistance()
|
||||
{
|
||||
var minDistance = MinLineDistance * 360d / (Math.Pow(2d, ParentMap.ZoomLevel) * TileSource.TileSize);
|
||||
var minDistance = MinLineDistance / MapProjection.DegreesToViewportScale(ParentMap.ZoomLevel);
|
||||
var scale = 1d;
|
||||
|
||||
if (minDistance < 1d)
|
||||
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
#if NETFX_CORE
|
||||
#if WINDOWS_UWP
|
||||
using Windows.Foundation;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
|
|
@ -279,7 +279,7 @@ namespace MapControl
|
|||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine(ex.Message);
|
||||
Debug.WriteLine("MapImageLayer: " + ex.Message);
|
||||
}
|
||||
|
||||
if (!imageUpdated)
|
||||
|
|
@ -295,12 +295,12 @@ namespace MapControl
|
|||
/// </summary>
|
||||
protected abstract bool UpdateImage(BoundingBox boundingBox);
|
||||
|
||||
private void SetTopImage(BitmapSource bitmap)
|
||||
private void SetTopImage(BitmapSource bitmapSource)
|
||||
{
|
||||
topImageIndex = (topImageIndex + 1) % 2;
|
||||
var topImage = (Image)Children[topImageIndex];
|
||||
|
||||
topImage.Source = bitmap;
|
||||
topImage.Source = bitmapSource;
|
||||
SetBoundingBox(topImage, boundingBox?.Clone());
|
||||
}
|
||||
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
// © 2017 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
#if NETFX_CORE
|
||||
#if WINDOWS_UWP
|
||||
using Windows.UI.Text;
|
||||
using Windows.UI.Xaml.Media;
|
||||
#else
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using System;
|
||||
#if NETFX_CORE
|
||||
#if WINDOWS_UWP
|
||||
using Windows.Foundation;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
// © 2017 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
#if NETFX_CORE
|
||||
#if WINDOWS_UWP
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Media;
|
||||
#else
|
||||
|
|
@ -4,11 +4,10 @@
|
|||
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
#if NETFX_CORE
|
||||
#if WINDOWS_UWP
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Media;
|
||||
#else
|
||||
using System.ComponentModel;
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
#endif
|
||||
|
|
@ -28,18 +27,6 @@ namespace MapControl
|
|||
nameof(IsClosed), typeof(bool), typeof(MapPolyline),
|
||||
new PropertyMetadata(false, (o, e) => ((MapPolyline)o).UpdateData()));
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the locations that define the polyline points.
|
||||
/// </summary>
|
||||
#if !NETFX_CORE
|
||||
[TypeConverter(typeof(LocationCollectionConverter))]
|
||||
#endif
|
||||
public IEnumerable<Location> Locations
|
||||
{
|
||||
get { return (IEnumerable<Location>)GetValue(LocationsProperty); }
|
||||
set { SetValue(LocationsProperty, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value that indicates if the polyline is closed, i.e. is a polygon.
|
||||
/// </summary>
|
||||
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
#if NETFX_CORE
|
||||
#if WINDOWS_UWP
|
||||
using Windows.Foundation;
|
||||
using Windows.UI.Xaml.Media;
|
||||
#else
|
||||
|
|
@ -15,14 +15,22 @@ using System.Windows.Media;
|
|||
namespace MapControl
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines a map projection between geographic coordinates and cartesian map coordinates
|
||||
/// and viewport coordinates, i.e. pixels.
|
||||
/// Defines a map projection between geographic coordinates, cartesian map coordinates and viewport coordinates.
|
||||
/// </summary>
|
||||
public abstract partial class MapProjection
|
||||
public abstract class MapProjection
|
||||
{
|
||||
public const int TileSize = 256;
|
||||
public const double Wgs84EquatorialRadius = 6378137d;
|
||||
public const double MetersPerDegree = Wgs84EquatorialRadius * Math.PI / 180d;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the scaling factor from cartesian map coordinates in degrees to viewport coordinates for the specified zoom level.
|
||||
/// </summary>
|
||||
public static double DegreesToViewportScale(double zoomLevel)
|
||||
{
|
||||
return Math.Pow(2d, zoomLevel) * TileSize / 360d;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the WMS 1.3.0 CRS Identifier.
|
||||
/// </summary>
|
||||
|
|
@ -64,7 +72,7 @@ namespace MapControl
|
|||
/// </summary>
|
||||
public virtual double GetViewportScale(double zoomLevel)
|
||||
{
|
||||
return Math.Pow(2d, zoomLevel) * TileSource.TileSize / 360d;
|
||||
return DegreesToViewportScale(zoomLevel);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -142,7 +150,7 @@ namespace MapControl
|
|||
var center = LocationToPoint(mapCenter);
|
||||
|
||||
ViewportTransform.Matrix = MatrixEx.TranslateScaleRotateTranslate(
|
||||
center.X, center.Y, ViewportScale, -ViewportScale, heading, viewportCenter.X, viewportCenter.Y);
|
||||
-center.X, -center.Y, ViewportScale, -ViewportScale, heading, viewportCenter.X, viewportCenter.Y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -5,16 +5,14 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
#if NETFX_CORE
|
||||
#if WINDOWS_UWP
|
||||
using Windows.Foundation;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Markup;
|
||||
using Windows.UI.Xaml.Media;
|
||||
#else
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Markup;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Threading;
|
||||
#endif
|
||||
|
|
@ -29,11 +27,6 @@ namespace MapControl
|
|||
/// <summary>
|
||||
/// Fills the map viewport with map tiles from a TileSource.
|
||||
/// </summary>
|
||||
#if NETFX_CORE
|
||||
[ContentProperty(Name = "TileSource")]
|
||||
#else
|
||||
[ContentProperty("TileSource")]
|
||||
#endif
|
||||
public partial class MapTileLayer : Panel, IMapLayer
|
||||
{
|
||||
/// <summary>
|
||||
|
|
@ -253,9 +246,9 @@ namespace MapControl
|
|||
{
|
||||
foreach (var tile in Tiles)
|
||||
{
|
||||
var tileSize = TileSource.TileSize << (TileGrid.ZoomLevel - tile.ZoomLevel);
|
||||
var x = tileSize * tile.X - TileSource.TileSize * TileGrid.XMin;
|
||||
var y = tileSize * tile.Y - TileSource.TileSize * TileGrid.YMin;
|
||||
var tileSize = MapProjection.TileSize << (TileGrid.ZoomLevel - tile.ZoomLevel);
|
||||
var x = tileSize * tile.X - MapProjection.TileSize * TileGrid.XMin;
|
||||
var y = tileSize * tile.Y - MapProjection.TileSize * TileGrid.YMin;
|
||||
|
||||
tile.Image.Width = tileSize;
|
||||
tile.Image.Height = tileSize;
|
||||
|
|
@ -322,8 +315,8 @@ namespace MapControl
|
|||
private TileGrid GetTileGrid()
|
||||
{
|
||||
var tileZoomLevel = Math.Max(0, (int)Math.Round(parentMap.ZoomLevel + ZoomLevelOffset));
|
||||
var tileScale = (1 << tileZoomLevel);
|
||||
var scale = tileScale / (Math.Pow(2d, parentMap.ZoomLevel) * TileSource.TileSize);
|
||||
var tileScale = (double)(1 << tileZoomLevel);
|
||||
var scale = tileScale / (Math.Pow(2d, parentMap.ZoomLevel) * MapProjection.TileSize);
|
||||
var tileCenterX = tileScale * (0.5 + parentMap.Center.Longitude / 360d);
|
||||
var tileCenterY = tileScale * (0.5 - WebMercatorProjection.LatitudeToY(parentMap.Center.Latitude) / 360d);
|
||||
var viewCenterX = parentMap.RenderSize.Width / 2d;
|
||||
|
|
@ -331,7 +324,7 @@ namespace MapControl
|
|||
|
||||
var transform = new MatrixTransform
|
||||
{
|
||||
Matrix = MatrixEx.TranslateScaleRotateTranslate(viewCenterX, viewCenterY, scale, scale, -parentMap.Heading, tileCenterX, tileCenterY)
|
||||
Matrix = MatrixEx.TranslateScaleRotateTranslate(-viewCenterX, -viewCenterY, scale, scale, -parentMap.Heading, tileCenterX, tileCenterY)
|
||||
};
|
||||
|
||||
var bounds = transform.TransformBounds(new Rect(0d, 0d, parentMap.RenderSize.Width, parentMap.RenderSize.Height));
|
||||
|
|
@ -343,17 +336,17 @@ namespace MapControl
|
|||
|
||||
private void SetRenderTransform()
|
||||
{
|
||||
var tileScale = (1 << TileGrid.ZoomLevel);
|
||||
var tileScale = (double)(1 << TileGrid.ZoomLevel);
|
||||
var scale = Math.Pow(2d, parentMap.ZoomLevel) / tileScale;
|
||||
var tileCenterX = tileScale * (0.5 + parentMap.Center.Longitude / 360d);
|
||||
var tileCenterY = tileScale * (0.5 - WebMercatorProjection.LatitudeToY(parentMap.Center.Latitude) / 360d);
|
||||
var tileOriginX = TileSource.TileSize * (tileCenterX - TileGrid.XMin);
|
||||
var tileOriginY = TileSource.TileSize * (tileCenterY - TileGrid.YMin);
|
||||
var tileOriginX = MapProjection.TileSize * (tileCenterX - TileGrid.XMin);
|
||||
var tileOriginY = MapProjection.TileSize * (tileCenterY - TileGrid.YMin);
|
||||
var viewCenterX = parentMap.RenderSize.Width / 2d;
|
||||
var viewCenterY = parentMap.RenderSize.Height / 2d;
|
||||
|
||||
((MatrixTransform)RenderTransform).Matrix = MatrixEx.TranslateScaleRotateTranslate(
|
||||
tileOriginX, tileOriginY, scale, scale, parentMap.Heading, viewCenterX, viewCenterY);
|
||||
-tileOriginX, -tileOriginY, scale, scale, parentMap.Heading, viewCenterX, viewCenterY);
|
||||
}
|
||||
|
||||
private void UpdateTiles()
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using System;
|
||||
#if NETFX_CORE
|
||||
#if WINDOWS_UWP
|
||||
using Windows.Foundation;
|
||||
#else
|
||||
using System.Windows;
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using System;
|
||||
#if NETFX_CORE
|
||||
#if WINDOWS_UWP
|
||||
using Windows.Foundation;
|
||||
#else
|
||||
using System.Windows;
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using System;
|
||||
#if NETFX_CORE
|
||||
#if WINDOWS_UWP
|
||||
using Windows.UI.Xaml.Controls;
|
||||
#else
|
||||
using System.Windows.Controls;
|
||||
|
|
@ -25,13 +25,12 @@ namespace MapControl
|
|||
|
||||
public bool Equals(TileGrid tileGrid)
|
||||
{
|
||||
return ReferenceEquals(this, tileGrid)
|
||||
|| (tileGrid != null
|
||||
return tileGrid != null
|
||||
&& tileGrid.ZoomLevel == ZoomLevel
|
||||
&& tileGrid.XMin == XMin
|
||||
&& tileGrid.YMin == YMin
|
||||
&& tileGrid.XMax == XMax
|
||||
&& tileGrid.YMax == YMax);
|
||||
&& tileGrid.YMax == YMax;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
172
MapControl/Shared/TileImageLoader.cs
Normal file
172
MapControl/Shared/TileImageLoader.cs
Normal file
|
|
@ -0,0 +1,172 @@
|
|||
// 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.Collections.Concurrent;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
#if WINDOWS_UWP
|
||||
using Windows.Web.Http;
|
||||
#else
|
||||
using System.Net.Http;
|
||||
#endif
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
/// <summary>
|
||||
/// Loads and optionally caches map tile images for a MapTileLayer.
|
||||
/// </summary>
|
||||
public partial class TileImageLoader : ITileImageLoader
|
||||
{
|
||||
/// <summary>
|
||||
/// The HttpClient instance used when image data is downloaded from a web resource.
|
||||
/// </summary>
|
||||
public static HttpClient HttpClient { get; set; } = new HttpClient();
|
||||
|
||||
/// <summary>
|
||||
/// Default expiration time for cached tile images. Used when no expiration time
|
||||
/// was transmitted on download. The default value is one day.
|
||||
/// </summary>
|
||||
public static TimeSpan DefaultCacheExpiration { get; set; } = TimeSpan.FromDays(1);
|
||||
|
||||
/// <summary>
|
||||
/// Minimum expiration time for cached tile images. The default value is one hour.
|
||||
/// </summary>
|
||||
public static TimeSpan MinimumCacheExpiration { get; set; } = TimeSpan.FromHours(1);
|
||||
|
||||
/// <summary>
|
||||
/// Maximum expiration time for cached tile images. The default value is one week.
|
||||
/// </summary>
|
||||
public static TimeSpan MaximumCacheExpiration { get; set; } = TimeSpan.FromDays(7);
|
||||
|
||||
/// <summary>
|
||||
/// Format string for creating cache keys from the SourceName property of a TileSource,
|
||||
/// the ZoomLevel, XIndex, and Y properties of a Tile, and the image file extension.
|
||||
/// The default value is "{0};{1};{2};{3}{4}".
|
||||
/// </summary>
|
||||
public static string CacheKeyFormat { get; set; } = "{0};{1};{2};{3}{4}";
|
||||
|
||||
private const string bingMapsTileInfo = "X-VE-Tile-Info";
|
||||
private const string bingMapsNoTile = "no-tile";
|
||||
|
||||
private readonly ConcurrentStack<Tile> pendingTiles = new ConcurrentStack<Tile>();
|
||||
private int taskCount;
|
||||
|
||||
public void LoadTiles(MapTileLayer tileLayer)
|
||||
{
|
||||
pendingTiles.Clear();
|
||||
|
||||
var tileSource = tileLayer.TileSource;
|
||||
var sourceName = tileLayer.SourceName;
|
||||
var tiles = tileLayer.Tiles.Where(t => t.Pending);
|
||||
|
||||
if (tileSource != null && tiles.Any())
|
||||
{
|
||||
if (Cache == null || string.IsNullOrEmpty(sourceName) ||
|
||||
tileSource.UriFormat == null || !tileSource.UriFormat.StartsWith("http"))
|
||||
{
|
||||
// no caching, load tile images in UI thread
|
||||
|
||||
foreach (var tile in tiles)
|
||||
{
|
||||
LoadTileImage(tileSource, tile);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pendingTiles.PushRange(tiles.Reverse().ToArray());
|
||||
|
||||
while (taskCount < Math.Min(pendingTiles.Count, tileLayer.MaxParallelDownloads))
|
||||
{
|
||||
Interlocked.Increment(ref taskCount);
|
||||
|
||||
var task = Task.Run(async () => // do not await
|
||||
{
|
||||
await LoadPendingTilesAsync(tileSource, sourceName); // run multiple times in parallel
|
||||
|
||||
Interlocked.Decrement(ref taskCount);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void LoadTileImage(TileSource tileSource, Tile tile)
|
||||
{
|
||||
tile.Pending = false;
|
||||
|
||||
try
|
||||
{
|
||||
var imageSource = tileSource.LoadImage(tile.XIndex, tile.Y, tile.ZoomLevel);
|
||||
|
||||
if (imageSource != null)
|
||||
{
|
||||
tile.SetImage(imageSource);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine("TileImageLoader: {0}/{1}/{2}: {3}", tile.ZoomLevel, tile.XIndex, tile.Y, ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task LoadPendingTilesAsync(TileSource tileSource, string sourceName)
|
||||
{
|
||||
Tile tile;
|
||||
|
||||
while (pendingTiles.TryPop(out tile))
|
||||
{
|
||||
tile.Pending = false;
|
||||
|
||||
try
|
||||
{
|
||||
var uri = tileSource.GetUri(tile.XIndex, tile.Y, tile.ZoomLevel);
|
||||
|
||||
if (uri != null)
|
||||
{
|
||||
var extension = Path.GetExtension(uri.LocalPath);
|
||||
|
||||
if (string.IsNullOrEmpty(extension) || extension == ".jpeg")
|
||||
{
|
||||
extension = ".jpg";
|
||||
}
|
||||
|
||||
var cacheKey = string.Format(CacheKeyFormat, sourceName, tile.ZoomLevel, tile.XIndex, tile.Y, extension);
|
||||
|
||||
await LoadTileImageAsync(tile, uri, cacheKey);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine("TileImageLoader: {0}/{1}/{2}: {3}", tile.ZoomLevel, tile.XIndex, tile.Y, ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static DateTime GetExpiration(HttpResponseMessage response)
|
||||
{
|
||||
var expiration = DefaultCacheExpiration;
|
||||
var headers = response.Headers;
|
||||
|
||||
if (headers.CacheControl != null && headers.CacheControl.MaxAge.HasValue)
|
||||
{
|
||||
expiration = headers.CacheControl.MaxAge.Value;
|
||||
|
||||
if (expiration < MinimumCacheExpiration)
|
||||
{
|
||||
expiration = MinimumCacheExpiration;
|
||||
}
|
||||
else if (expiration > MaximumCacheExpiration)
|
||||
{
|
||||
expiration = MaximumCacheExpiration;
|
||||
}
|
||||
}
|
||||
|
||||
return DateTime.UtcNow.Add(expiration);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,16 +4,21 @@
|
|||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
#if WINDOWS_UWP
|
||||
using Windows.UI.Xaml.Media;
|
||||
using Windows.UI.Xaml.Media.Imaging;
|
||||
#else
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
#endif
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides the URI of a map tile.
|
||||
/// Provides the download Uri or ImageSource of map tiles.
|
||||
/// </summary>
|
||||
public partial class TileSource
|
||||
{
|
||||
public const int TileSize = 256;
|
||||
|
||||
private Func<int, int, int, Uri> getUri;
|
||||
private string uriFormat = string.Empty;
|
||||
|
||||
|
|
@ -26,6 +31,9 @@ namespace MapControl
|
|||
this.uriFormat = uriFormat;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the format string to produce tile Uris.
|
||||
/// </summary>
|
||||
public string UriFormat
|
||||
{
|
||||
get { return uriFormat; }
|
||||
|
|
@ -76,11 +84,25 @@ namespace MapControl
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the map tile Uri.
|
||||
/// </summary>
|
||||
public virtual Uri GetUri(int x, int y, int zoomLevel)
|
||||
{
|
||||
return getUri?.Invoke(x, y, zoomLevel);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the map tile ImageSource without caching in TileImageLoader.Cache.
|
||||
/// By overriding LoadImage an application can provide arbitrary tile images.
|
||||
/// </summary>
|
||||
public virtual ImageSource LoadImage(int x, int y, int zoomLevel)
|
||||
{
|
||||
var uri = GetUri(x, y, zoomLevel);
|
||||
|
||||
return uri != null ? new BitmapImage(uri) : null;
|
||||
}
|
||||
|
||||
private Uri GetBasicUri(int x, int y, int zoomLevel)
|
||||
{
|
||||
return new Uri(uriFormat
|
||||
|
|
@ -170,8 +192,8 @@ namespace MapControl
|
|||
.Replace("{S}", south.ToString(CultureInfo.InvariantCulture))
|
||||
.Replace("{E}", east.ToString(CultureInfo.InvariantCulture))
|
||||
.Replace("{N}", north.ToString(CultureInfo.InvariantCulture))
|
||||
.Replace("{X}", TileSize.ToString())
|
||||
.Replace("{Y}", TileSize.ToString()));
|
||||
.Replace("{X}", MapProjection.TileSize.ToString())
|
||||
.Replace("{Y}", MapProjection.TileSize.ToString()));
|
||||
}
|
||||
|
||||
private Uri GetLatLonBoundingBoxUri(int x, int y, int zoomLevel)
|
||||
|
|
@ -187,8 +209,8 @@ namespace MapControl
|
|||
.Replace("{s}", south.ToString(CultureInfo.InvariantCulture))
|
||||
.Replace("{e}", east.ToString(CultureInfo.InvariantCulture))
|
||||
.Replace("{n}", north.ToString(CultureInfo.InvariantCulture))
|
||||
.Replace("{X}", TileSize.ToString())
|
||||
.Replace("{Y}", TileSize.ToString()));
|
||||
.Replace("{X}", MapProjection.TileSize.ToString())
|
||||
.Replace("{Y}", MapProjection.TileSize.ToString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using System;
|
||||
#if NETFX_CORE
|
||||
#if WINDOWS_UWP
|
||||
using Windows.Foundation;
|
||||
#else
|
||||
using System.Windows;
|
||||
|
|
@ -34,7 +34,7 @@ namespace MapControl
|
|||
|
||||
public override double GetViewportScale(double zoomLevel)
|
||||
{
|
||||
return base.GetViewportScale(zoomLevel) / MetersPerDegree;
|
||||
return DegreesToViewportScale(zoomLevel) / MetersPerDegree;
|
||||
}
|
||||
|
||||
public override Point GetMapScale(Location location)
|
||||
|
|
@ -70,6 +70,8 @@ namespace MapControl
|
|||
|
||||
public static double LatitudeToY(double latitude)
|
||||
{
|
||||
var lat = latitude * Math.PI / 180d;
|
||||
|
||||
return latitude <= -90d ? double.NegativeInfinity
|
||||
: latitude >= 90d ? double.PositiveInfinity
|
||||
: Math.Log(Math.Tan((latitude + 90d) * Math.PI / 360d)) / Math.PI * 180d;
|
||||
|
|
@ -3,10 +3,16 @@
|
|||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using System;
|
||||
#if NETFX_CORE
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
#if WINDOWS_UWP
|
||||
using Windows.Data.Xml.Dom;
|
||||
using Windows.UI.Xaml;
|
||||
#else
|
||||
using System.Windows;
|
||||
using System.Xml;
|
||||
#endif
|
||||
|
||||
namespace MapControl
|
||||
|
|
@ -23,15 +29,11 @@ namespace MapControl
|
|||
|
||||
public static readonly DependencyProperty LayersProperty = DependencyProperty.Register(
|
||||
nameof(Layers), typeof(string), typeof(WmsImageLayer),
|
||||
new PropertyMetadata(null, (o, e) => ((WmsImageLayer)o).UpdateImage()));
|
||||
new PropertyMetadata(string.Empty, (o, e) => ((WmsImageLayer)o).UpdateImage()));
|
||||
|
||||
public static readonly DependencyProperty StylesProperty = DependencyProperty.Register(
|
||||
nameof(Styles), typeof(string), typeof(WmsImageLayer),
|
||||
new PropertyMetadata(null, (o, e) => ((WmsImageLayer)o).UpdateImage()));
|
||||
|
||||
public static readonly DependencyProperty ParametersProperty = DependencyProperty.Register(
|
||||
nameof(Parameters), typeof(string), typeof(WmsImageLayer),
|
||||
new PropertyMetadata(null, (o, e) => ((WmsImageLayer)o).UpdateImage()));
|
||||
new PropertyMetadata(string.Empty, (o, e) => ((WmsImageLayer)o).UpdateImage()));
|
||||
|
||||
public static readonly DependencyProperty FormatProperty = DependencyProperty.Register(
|
||||
nameof(Format), typeof(string), typeof(WmsImageLayer),
|
||||
|
|
@ -67,12 +69,6 @@ namespace MapControl
|
|||
set { SetValue(StylesProperty, value); }
|
||||
}
|
||||
|
||||
public string Parameters
|
||||
{
|
||||
get { return (string)GetValue(ParametersProperty); }
|
||||
set { SetValue(ParametersProperty, value); }
|
||||
}
|
||||
|
||||
public string Format
|
||||
{
|
||||
get { return (string)GetValue(FormatProperty); }
|
||||
|
|
@ -92,14 +88,60 @@ namespace MapControl
|
|||
return false;
|
||||
}
|
||||
|
||||
var version = Version ?? "1.3.0";
|
||||
var queryParameters = ParentMap.MapProjection.WmsQueryParameters(boundingBox, version);
|
||||
var projectionParameters = ParentMap.MapProjection.WmsQueryParameters(boundingBox, Version);
|
||||
|
||||
if (string.IsNullOrEmpty(queryParameters))
|
||||
if (string.IsNullOrEmpty(projectionParameters))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
UpdateImage(GetRequestUri("GetMap"
|
||||
+ "&LAYERS=" + Layers + "&STYLES=" + Styles + "&FORMAT=" + Format
|
||||
+ "&TRANSPARENT=" + (Transparent ? "TRUE" : "FALSE") + "&" + projectionParameters));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public async Task<IList<string>> GetLayerNamesAsync()
|
||||
{
|
||||
if (ServerUri == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var layerNames = new List<string>();
|
||||
|
||||
try
|
||||
{
|
||||
var document = await XmlDocument.LoadFromUriAsync(GetRequestUri("GetCapabilities"));
|
||||
|
||||
var capability = ChildElements(document.DocumentElement, "Capability").FirstOrDefault();
|
||||
if (capability != null)
|
||||
{
|
||||
var rootLayer = ChildElements(capability, "Layer").FirstOrDefault();
|
||||
if (rootLayer != null)
|
||||
{
|
||||
foreach (var layer in ChildElements(rootLayer, "Layer"))
|
||||
{
|
||||
var name = ChildElements(layer, "Name").FirstOrDefault();
|
||||
if (name != null)
|
||||
{
|
||||
layerNames.Add(name.InnerText);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine("WmsImageLayer: {0}: {1}", ServerUri, ex.Message);
|
||||
}
|
||||
|
||||
return layerNames;
|
||||
}
|
||||
|
||||
private Uri GetRequestUri(string query)
|
||||
{
|
||||
var uri = ServerUri.ToString();
|
||||
|
||||
if (!uri.EndsWith("?") && !uri.EndsWith("&"))
|
||||
|
|
@ -107,22 +149,14 @@ namespace MapControl
|
|||
uri += "?";
|
||||
}
|
||||
|
||||
uri += "SERVICE=WMS"
|
||||
+ "&VERSION=" + version
|
||||
+ "&REQUEST=GetMap"
|
||||
+ "&LAYERS=" + (Layers ?? string.Empty)
|
||||
+ "&STYLES=" + (Styles ?? string.Empty)
|
||||
+ "&" + queryParameters
|
||||
+ "&FORMAT=" + (Format ?? "image/png")
|
||||
+ "&TRANSPARENT=" + (Transparent ? "TRUE" : "FALSE");
|
||||
uri += "SERVICE=WMS&VERSION=" + Version + "&REQUEST=" + query;
|
||||
|
||||
if (!string.IsNullOrEmpty(Parameters))
|
||||
{
|
||||
uri += "&" + Parameters;
|
||||
}
|
||||
return new Uri(uri.Replace(" ", "%20"));
|
||||
}
|
||||
|
||||
UpdateImage(new Uri(uri.Replace(" ", "%20")));
|
||||
return true;
|
||||
private static IEnumerable<XmlElement> ChildElements(XmlElement element, string name)
|
||||
{
|
||||
return element.ChildNodes.OfType<XmlElement>().Where(e => (string)e.LocalName == name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,53 +0,0 @@
|
|||
// 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.Linq;
|
||||
using System.Diagnostics;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
/// <summary>
|
||||
/// Loads map tile images.
|
||||
/// </summary>
|
||||
public class TileImageLoader : ITileImageLoader
|
||||
{
|
||||
public void LoadTiles(MapTileLayer tileLayer)
|
||||
{
|
||||
var tileSource = tileLayer.TileSource;
|
||||
var imageTileSource = tileSource as ImageTileSource;
|
||||
|
||||
foreach (var tile in tileLayer.Tiles.Where(t => t.Pending))
|
||||
{
|
||||
tile.Pending = false;
|
||||
|
||||
try
|
||||
{
|
||||
ImageSource image = null;
|
||||
Uri uri;
|
||||
|
||||
if (imageTileSource != null)
|
||||
{
|
||||
image = imageTileSource.LoadImage(tile.XIndex, tile.Y, tile.ZoomLevel);
|
||||
}
|
||||
else if ((uri = tileSource.GetUri(tile.XIndex, tile.Y, tile.ZoomLevel)) != null)
|
||||
{
|
||||
image = new BitmapImage(uri);
|
||||
}
|
||||
|
||||
if (image != null)
|
||||
{
|
||||
tile.SetImage(image);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine("{0}/{1}/{2}: {3}", tile.ZoomLevel, tile.XIndex, tile.Y, ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,272 +0,0 @@
|
|||
// 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.Collections.Concurrent;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Runtime.Caching;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
/// <summary>
|
||||
/// Loads map tile images and optionally caches them in a System.Runtime.Caching.ObjectCache.
|
||||
/// </summary>
|
||||
public class TileImageLoader : ITileImageLoader
|
||||
{
|
||||
/// <summary>
|
||||
/// Default name of an ObjectCache instance that is assigned to the Cache property.
|
||||
/// </summary>
|
||||
public const string DefaultCacheName = "TileCache";
|
||||
|
||||
/// <summary>
|
||||
/// Default folder path where an ObjectCache instance may save cached data.
|
||||
/// </summary>
|
||||
public static readonly string DefaultCacheFolder =
|
||||
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "MapControl");
|
||||
|
||||
/// <summary>
|
||||
/// Default expiration time for cached tile images. Used when no expiration time
|
||||
/// was transmitted on download. The default value is one day.
|
||||
/// </summary>
|
||||
public static TimeSpan DefaultCacheExpiration { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Minimum expiration time for cached tile images. Used when an unnecessarily small expiration time
|
||||
/// was transmitted on download (e.g. Cache-Control: max-age=0). The default value is one hour.
|
||||
/// </summary>
|
||||
public static TimeSpan MinimumCacheExpiration { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The ObjectCache used to cache tile images. The default is MemoryCache.Default.
|
||||
/// </summary>
|
||||
public static ObjectCache Cache { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Optional value to be used for the HttpWebRequest.UserAgent property. The default is null.
|
||||
/// </summary>
|
||||
public static string HttpUserAgent { get; set; }
|
||||
|
||||
static TileImageLoader()
|
||||
{
|
||||
DefaultCacheExpiration = TimeSpan.FromDays(1);
|
||||
MinimumCacheExpiration = TimeSpan.FromHours(1);
|
||||
Cache = MemoryCache.Default;
|
||||
}
|
||||
|
||||
private readonly ConcurrentStack<Tile> pendingTiles = new ConcurrentStack<Tile>();
|
||||
private int taskCount;
|
||||
|
||||
public void LoadTiles(MapTileLayer tileLayer)
|
||||
{
|
||||
pendingTiles.Clear();
|
||||
|
||||
var tiles = tileLayer.Tiles.Where(t => t.Pending);
|
||||
|
||||
if (tiles.Any())
|
||||
{
|
||||
pendingTiles.PushRange(tiles.Reverse().ToArray());
|
||||
|
||||
var tileSource = tileLayer.TileSource;
|
||||
var sourceName = tileLayer.SourceName;
|
||||
var maxDownloads = tileLayer.MaxParallelDownloads;
|
||||
|
||||
while (taskCount < Math.Min(pendingTiles.Count, maxDownloads))
|
||||
{
|
||||
Interlocked.Increment(ref taskCount);
|
||||
|
||||
Task.Run(() =>
|
||||
{
|
||||
LoadPendingTiles(tileSource, sourceName);
|
||||
|
||||
Interlocked.Decrement(ref taskCount);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void LoadPendingTiles(TileSource tileSource, string sourceName)
|
||||
{
|
||||
var imageTileSource = tileSource as ImageTileSource;
|
||||
Tile tile;
|
||||
|
||||
while (pendingTiles.TryPop(out tile))
|
||||
{
|
||||
tile.Pending = false;
|
||||
|
||||
try
|
||||
{
|
||||
ImageSource image = null;
|
||||
Uri uri;
|
||||
|
||||
if (imageTileSource != null)
|
||||
{
|
||||
image = imageTileSource.LoadImage(tile.XIndex, tile.Y, tile.ZoomLevel);
|
||||
}
|
||||
else if ((uri = tileSource.GetUri(tile.XIndex, tile.Y, tile.ZoomLevel)) != null)
|
||||
{
|
||||
image = LoadImage(uri, sourceName, tile.XIndex, tile.Y, tile.ZoomLevel);
|
||||
}
|
||||
|
||||
if (image != null)
|
||||
{
|
||||
tile.SetImage(image);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine("{0}/{1}/{2}: {3}", tile.ZoomLevel, tile.XIndex, tile.Y, ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private ImageSource LoadImage(Uri uri, string sourceName, int x, int y, int zoomLevel)
|
||||
{
|
||||
ImageSource image = null;
|
||||
|
||||
try
|
||||
{
|
||||
if (!uri.IsAbsoluteUri)
|
||||
{
|
||||
image = BitmapSourceHelper.FromFile(uri.OriginalString);
|
||||
}
|
||||
else if (uri.Scheme == "file")
|
||||
{
|
||||
image = BitmapSourceHelper.FromFile(uri.LocalPath);
|
||||
}
|
||||
else if (Cache == null || string.IsNullOrEmpty(sourceName))
|
||||
{
|
||||
image = DownloadImage(uri, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
var cacheKey = string.Format("{0}/{1}/{2}/{3}", sourceName, zoomLevel, x, y);
|
||||
|
||||
if (!GetCachedImage(cacheKey, ref image))
|
||||
{
|
||||
// Either no cached image was found or expiration time has expired.
|
||||
// If download fails use possibly cached but expired image anyway.
|
||||
image = DownloadImage(uri, cacheKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (WebException ex)
|
||||
{
|
||||
Debug.WriteLine("{0}: {1}: {2}", uri, ex.Status, ex.Message);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine("{0}: {1}", uri, ex.Message);
|
||||
}
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
private static ImageSource DownloadImage(Uri uri, string cacheKey)
|
||||
{
|
||||
ImageSource image = null;
|
||||
var request = WebRequest.CreateHttp(uri);
|
||||
|
||||
if (HttpUserAgent != null)
|
||||
{
|
||||
request.UserAgent = HttpUserAgent;
|
||||
}
|
||||
|
||||
using (var response = (HttpWebResponse)request.GetResponse())
|
||||
{
|
||||
if (response.Headers["X-VE-Tile-Info"] != "no-tile") // set by Bing Maps
|
||||
{
|
||||
using (var responseStream = response.GetResponseStream())
|
||||
using (var memoryStream = new MemoryStream())
|
||||
{
|
||||
responseStream.CopyTo(memoryStream);
|
||||
memoryStream.Seek(0, SeekOrigin.Begin);
|
||||
image = BitmapSourceHelper.FromStream(memoryStream);
|
||||
|
||||
if (cacheKey != null)
|
||||
{
|
||||
SetCachedImage(cacheKey, memoryStream, GetExpiration(response.Headers));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
private static bool GetCachedImage(string cacheKey, ref ImageSource image)
|
||||
{
|
||||
var result = false;
|
||||
var buffer = Cache.Get(cacheKey) as byte[];
|
||||
|
||||
if (buffer != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var memoryStream = new MemoryStream(buffer))
|
||||
{
|
||||
image = BitmapSourceHelper.FromStream(memoryStream);
|
||||
}
|
||||
|
||||
DateTime expiration = DateTime.MinValue;
|
||||
|
||||
if (buffer.Length >= 16 && Encoding.ASCII.GetString(buffer, buffer.Length - 16, 8) == "EXPIRES:")
|
||||
{
|
||||
expiration = new DateTime(BitConverter.ToInt64(buffer, buffer.Length - 8), DateTimeKind.Utc);
|
||||
}
|
||||
|
||||
result = expiration > DateTime.UtcNow;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine("{0}: {1}", cacheKey, ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void SetCachedImage(string cacheKey, MemoryStream memoryStream, DateTime expiration)
|
||||
{
|
||||
memoryStream.Seek(0, SeekOrigin.End);
|
||||
memoryStream.Write(Encoding.ASCII.GetBytes("EXPIRES:"), 0, 8);
|
||||
memoryStream.Write(BitConverter.GetBytes(expiration.Ticks), 0, 8);
|
||||
|
||||
Cache.Set(cacheKey, memoryStream.ToArray(), new CacheItemPolicy { AbsoluteExpiration = expiration });
|
||||
}
|
||||
|
||||
private static DateTime GetExpiration(WebHeaderCollection headers)
|
||||
{
|
||||
var expiration = DefaultCacheExpiration;
|
||||
var cacheControl = headers["Cache-Control"];
|
||||
|
||||
if (cacheControl != null)
|
||||
{
|
||||
int maxAgeValue;
|
||||
var maxAgeDirective = cacheControl
|
||||
.Split(new char[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries)
|
||||
.FirstOrDefault(s => s.StartsWith("max-age="));
|
||||
|
||||
if (maxAgeDirective != null &&
|
||||
int.TryParse(maxAgeDirective.Substring(8), out maxAgeValue))
|
||||
{
|
||||
expiration = TimeSpan.FromSeconds(maxAgeValue);
|
||||
|
||||
if (expiration < MinimumCacheExpiration)
|
||||
{
|
||||
expiration = MinimumCacheExpiration;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return DateTime.UtcNow.Add(expiration);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,295 +0,0 @@
|
|||
// 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.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
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;
|
||||
using Windows.Web.Http.Filters;
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
/// <summary>
|
||||
/// Loads map tile images and optionally caches them in a IImageCache.
|
||||
/// </summary>
|
||||
public class TileImageLoader : ITileImageLoader
|
||||
{
|
||||
/// <summary>
|
||||
/// Default name of an IImageCache instance that is assigned to the Cache property.
|
||||
/// </summary>
|
||||
public const string DefaultCacheName = "TileCache";
|
||||
|
||||
/// <summary>
|
||||
/// Default StorageFolder where an IImageCache instance may save cached data.
|
||||
/// </summary>
|
||||
public static readonly StorageFolder DefaultCacheFolder = ApplicationData.Current.TemporaryFolder;
|
||||
|
||||
/// <summary>
|
||||
/// Default expiration time for cached tile images. Used when no expiration time
|
||||
/// was transmitted on download. The default value is one day.
|
||||
/// </summary>
|
||||
public static TimeSpan DefaultCacheExpiration { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Minimum expiration time for cached tile images. Used when an unnecessarily small expiration time
|
||||
/// was transmitted on download (e.g. Cache-Control: max-age=0). The default value is one hour.
|
||||
/// </summary>
|
||||
public static TimeSpan MinimumCacheExpiration { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The IImageCache implementation used to cache tile images. The default is null.
|
||||
/// </summary>
|
||||
public static Caching.IImageCache Cache;
|
||||
|
||||
static TileImageLoader()
|
||||
{
|
||||
DefaultCacheExpiration = TimeSpan.FromDays(1);
|
||||
MinimumCacheExpiration = TimeSpan.FromHours(1);
|
||||
}
|
||||
|
||||
private readonly ConcurrentStack<Tile> pendingTiles = new ConcurrentStack<Tile>();
|
||||
private int taskCount;
|
||||
|
||||
public void LoadTiles(MapTileLayer tileLayer)
|
||||
{
|
||||
pendingTiles.Clear();
|
||||
|
||||
var tiles = tileLayer.Tiles.Where(t => t.Pending);
|
||||
|
||||
if (tiles.Any())
|
||||
{
|
||||
var tileSource = tileLayer.TileSource;
|
||||
var imageTileSource = tileSource as ImageTileSource;
|
||||
|
||||
if (imageTileSource != null)
|
||||
{
|
||||
LoadTiles(tiles, imageTileSource);
|
||||
}
|
||||
else
|
||||
{
|
||||
pendingTiles.PushRange(tiles.Reverse().ToArray());
|
||||
|
||||
var sourceName = tileLayer.SourceName;
|
||||
var maxDownloads = tileLayer.MaxParallelDownloads;
|
||||
|
||||
while (taskCount < Math.Min(pendingTiles.Count, maxDownloads))
|
||||
{
|
||||
Interlocked.Increment(ref taskCount);
|
||||
|
||||
Task.Run(async () =>
|
||||
{
|
||||
await LoadPendingTiles(tileSource, sourceName);
|
||||
|
||||
Interlocked.Decrement(ref taskCount);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void LoadTiles(IEnumerable<Tile> tiles, ImageTileSource tileSource)
|
||||
{
|
||||
foreach (var tile in tiles)
|
||||
{
|
||||
tile.Pending = false;
|
||||
|
||||
try
|
||||
{
|
||||
var image = tileSource.LoadImage(tile.XIndex, tile.Y, tile.ZoomLevel);
|
||||
|
||||
if (image != null)
|
||||
{
|
||||
tile.SetImage(image);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine("{0}/{1}/{2}: {3}", tile.ZoomLevel, tile.XIndex, tile.Y, ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task LoadPendingTiles(TileSource tileSource, string sourceName)
|
||||
{
|
||||
Tile tile;
|
||||
|
||||
while (pendingTiles.TryPop(out tile))
|
||||
{
|
||||
tile.Pending = false;
|
||||
|
||||
try
|
||||
{
|
||||
var uri = tileSource.GetUri(tile.XIndex, tile.Y, tile.ZoomLevel);
|
||||
|
||||
if (uri != null)
|
||||
{
|
||||
if (!uri.IsAbsoluteUri)
|
||||
{
|
||||
await LoadImageFromFile(tile, uri.OriginalString);
|
||||
}
|
||||
else if (uri.Scheme == "file")
|
||||
{
|
||||
await LoadImageFromFile(tile, uri.LocalPath);
|
||||
}
|
||||
else if (Cache == null || sourceName == null)
|
||||
{
|
||||
await DownloadImage(tile, uri, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
var extension = Path.GetExtension(uri.LocalPath);
|
||||
|
||||
if (string.IsNullOrEmpty(extension) || extension == ".jpeg")
|
||||
{
|
||||
extension = ".jpg";
|
||||
}
|
||||
|
||||
var cacheKey = string.Format(@"{0}\{1}\{2}\{3}{4}", sourceName, tile.ZoomLevel, tile.XIndex, tile.Y, extension);
|
||||
var cacheItem = await Cache.GetAsync(cacheKey);
|
||||
var loaded = false;
|
||||
|
||||
if (cacheItem == null || cacheItem.Expiration <= DateTime.UtcNow)
|
||||
{
|
||||
loaded = await DownloadImage(tile, uri, cacheKey);
|
||||
}
|
||||
|
||||
if (!loaded && cacheItem != null && cacheItem.Buffer != null)
|
||||
{
|
||||
using (var stream = new InMemoryRandomAccessStream())
|
||||
{
|
||||
await stream.WriteAsync(cacheItem.Buffer);
|
||||
await stream.FlushAsync();
|
||||
stream.Seek(0);
|
||||
|
||||
await LoadImageFromStream(tile, stream);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine("{0}/{1}/{2}: {3}", tile.ZoomLevel, tile.XIndex, tile.Y, ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<bool> DownloadImage(Tile tile, Uri uri, string cacheKey)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var httpClient = new HttpClient(new HttpBaseProtocolFilter { AllowAutoRedirect = false }))
|
||||
using (var response = await httpClient.GetAsync(uri))
|
||||
{
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
string tileInfo;
|
||||
|
||||
if (!response.Headers.TryGetValue("X-VE-Tile-Info", out tileInfo) || tileInfo != "no-tile") // set by Bing Maps
|
||||
{
|
||||
await LoadImageFromHttpResponse(response, tile, cacheKey);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Debug.WriteLine("{0}: {1} {2}", uri, (int)response.StatusCode, response.ReasonPhrase);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine("{0}: {1}", uri, ex.Message);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private async Task LoadImageFromHttpResponse(HttpResponseMessage response, Tile tile, string cacheKey)
|
||||
{
|
||||
using (var stream = new InMemoryRandomAccessStream())
|
||||
{
|
||||
using (var content = response.Content)
|
||||
{
|
||||
await content.WriteToStreamAsync(stream);
|
||||
}
|
||||
|
||||
await stream.FlushAsync();
|
||||
stream.Seek(0);
|
||||
|
||||
await LoadImageFromStream(tile, stream);
|
||||
|
||||
if (cacheKey != null)
|
||||
{
|
||||
var buffer = new Windows.Storage.Streams.Buffer((uint)stream.Size);
|
||||
|
||||
stream.Seek(0);
|
||||
await stream.ReadAsync(buffer, buffer.Capacity, InputStreamOptions.None);
|
||||
|
||||
var expiration = DefaultCacheExpiration;
|
||||
|
||||
if (response.Headers.CacheControl.MaxAge.HasValue)
|
||||
{
|
||||
expiration = response.Headers.CacheControl.MaxAge.Value;
|
||||
|
||||
if (expiration < MinimumCacheExpiration)
|
||||
{
|
||||
expiration = MinimumCacheExpiration;
|
||||
}
|
||||
}
|
||||
|
||||
await Cache.SetAsync(cacheKey, buffer, DateTime.UtcNow.Add(expiration));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task LoadImageFromFile(Tile tile, string path)
|
||||
{
|
||||
try
|
||||
{
|
||||
var file = await StorageFile.GetFileFromPathAsync(path);
|
||||
|
||||
using (var stream = await file.OpenReadAsync())
|
||||
{
|
||||
await LoadImageFromStream(tile, stream);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine("{0}: {1}", path, ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task LoadImageFromStream(Tile tile, IRandomAccessStream stream)
|
||||
{
|
||||
var tcs = new TaskCompletionSource<object>();
|
||||
|
||||
await tile.Image.Dispatcher.RunAsync(CoreDispatcherPriority.Low, async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var image = new BitmapImage();
|
||||
await image.SetSourceAsync(stream);
|
||||
tile.SetImage(image, true, false);
|
||||
|
||||
tcs.SetResult(null);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
tcs.SetException(ex);
|
||||
}
|
||||
});
|
||||
|
||||
await tcs.Task;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
// 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.ComponentModel;
|
||||
using System.Globalization;
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
public class TileSourceConverter : TypeConverter
|
||||
{
|
||||
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
|
||||
{
|
||||
return sourceType == typeof(string);
|
||||
}
|
||||
|
||||
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
|
||||
{
|
||||
return new TileSource { UriFormat = value as string };
|
||||
}
|
||||
}
|
||||
|
||||
[TypeConverter(typeof(TileSourceConverter))]
|
||||
public partial class TileSource
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -2,10 +2,8 @@
|
|||
// © 2017 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Windows.Foundation;
|
||||
using Windows.UI.Core;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Media;
|
||||
using Windows.UI.Xaml.Media.Animation;
|
||||
|
|
@ -14,11 +12,6 @@ namespace MapControl
|
|||
{
|
||||
internal static class Extensions
|
||||
{
|
||||
public static IAsyncAction BeginInvoke(this CoreDispatcher dispatcher, Action action)
|
||||
{
|
||||
return dispatcher.RunAsync(CoreDispatcherPriority.Normal, new DispatchedHandler(action));
|
||||
}
|
||||
|
||||
public static Point Transform(this GeneralTransform transform, Point point)
|
||||
{
|
||||
return transform.TransformPoint(point);
|
||||
|
|
@ -47,6 +40,7 @@ namespace MapControl
|
|||
private static void BeginAnimation(DependencyObject obj, DependencyProperty property, Timeline animation)
|
||||
{
|
||||
string propertyName;
|
||||
|
||||
if (properties.TryGetValue(property, out propertyName))
|
||||
{
|
||||
Storyboard.SetTargetProperty(animation, propertyName);
|
||||
99
MapControl/UWP/ImageFileCache.UWP.cs
Normal file
99
MapControl/UWP/ImageFileCache.UWP.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,15 +2,10 @@
|
|||
// © 2017 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
#if NETFX_CORE
|
||||
using Windows.Foundation;
|
||||
using Windows.UI;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Media;
|
||||
#else
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
#endif
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
180
MapControl/UWP/MapControl.UWP.csproj
Normal file
180
MapControl/UWP/MapControl.UWP.csproj
Normal 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)' < '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>
|
||||
|
|
@ -3,20 +3,12 @@
|
|||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using System;
|
||||
#if NETFX_CORE
|
||||
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;
|
||||
#else
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Shapes;
|
||||
using System.Windows.Data;
|
||||
#endif
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
|
|
@ -3,15 +3,9 @@
|
|||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using System;
|
||||
#if NETFX_CORE
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Media.Imaging;
|
||||
#else
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media.Imaging;
|
||||
#endif
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
|
|
@ -19,14 +13,14 @@ namespace MapControl
|
|||
{
|
||||
protected void UpdateImage(Uri uri)
|
||||
{
|
||||
UpdateImage(new BitmapImage(uri));
|
||||
UpdateImage(uri != null ? new BitmapImage(uri) : null);
|
||||
}
|
||||
|
||||
protected void UpdateImage(BitmapSource bitmap)
|
||||
protected void UpdateImage(BitmapSource bitmapSource)
|
||||
{
|
||||
SetTopImage(bitmap);
|
||||
SetTopImage(bitmapSource);
|
||||
|
||||
var bitmapImage = bitmap as BitmapImage;
|
||||
var bitmapImage = bitmapSource as BitmapImage;
|
||||
|
||||
if (bitmapImage != null)
|
||||
{
|
||||
|
|
@ -41,18 +35,20 @@ namespace MapControl
|
|||
|
||||
private void BitmapImageOpened(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var bitmap = (BitmapImage)sender;
|
||||
bitmap.ImageOpened -= BitmapImageOpened;
|
||||
bitmap.ImageFailed -= BitmapImageFailed;
|
||||
var bitmapImage = (BitmapImage)sender;
|
||||
|
||||
bitmapImage.ImageOpened -= BitmapImageOpened;
|
||||
bitmapImage.ImageFailed -= BitmapImageFailed;
|
||||
|
||||
SwapImages();
|
||||
}
|
||||
|
||||
private void BitmapImageFailed(object sender, ExceptionRoutedEventArgs e)
|
||||
{
|
||||
var bitmap = (BitmapImage)sender;
|
||||
bitmap.ImageOpened -= BitmapImageOpened;
|
||||
bitmap.ImageFailed -= BitmapImageFailed;
|
||||
var bitmapImage = (BitmapImage)sender;
|
||||
|
||||
bitmapImage.ImageOpened -= BitmapImageOpened;
|
||||
bitmapImage.ImageFailed -= BitmapImageFailed;
|
||||
|
||||
((Image)Children[topImageIndex]).Source = null;
|
||||
SwapImages();
|
||||
|
|
@ -2,11 +2,7 @@
|
|||
// © 2017 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
#if NETFX_CORE
|
||||
using Windows.UI.Xaml.Controls;
|
||||
#else
|
||||
using System.Windows.Controls;
|
||||
#endif
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
|
|
@ -2,13 +2,8 @@
|
|||
// © 2017 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
#if NETFX_CORE
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
#else
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
#endif
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
|
|
@ -2,24 +2,13 @@
|
|||
// © 2017 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
#if NETFX_CORE
|
||||
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
|
||||
{
|
||||
#if NETFX_CORE
|
||||
class FontStyles { public const FontStyle Normal = FontStyle.Normal; }
|
||||
class FontStretches { public const FontStretch Normal = FontStretch.Normal; }
|
||||
#endif
|
||||
|
||||
public partial class MapOverlay
|
||||
{
|
||||
public static readonly DependencyProperty FontSizeProperty = DependencyProperty.Register(
|
||||
|
|
@ -29,10 +18,10 @@ namespace MapControl
|
|||
nameof(FontFamily), typeof(FontFamily), typeof(MapOverlay), new PropertyMetadata(null));
|
||||
|
||||
public static readonly DependencyProperty FontStyleProperty = DependencyProperty.Register(
|
||||
nameof(FontStyle), typeof(FontStyle), typeof(MapOverlay), new PropertyMetadata(FontStyles.Normal));
|
||||
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(FontStretches.Normal));
|
||||
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));
|
||||
|
|
@ -2,13 +2,8 @@
|
|||
// © 2017 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
#if NETFX_CORE
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Media;
|
||||
#else
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
#endif
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
|
|
@ -2,15 +2,9 @@
|
|||
// © 2017 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
#if NETFX_CORE
|
||||
using Windows.Foundation;
|
||||
using Windows.UI.Xaml.Media;
|
||||
using Windows.UI.Xaml.Shapes;
|
||||
#else
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Shapes;
|
||||
#endif
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
|
|
@ -23,7 +17,7 @@ namespace MapControl
|
|||
MapPanel.AddParentMapHandlers(this);
|
||||
}
|
||||
|
||||
protected override Size MeasureOverride(Size constraint)
|
||||
protected override Size MeasureOverride(Size availableSize)
|
||||
{
|
||||
if (Stretch != Stretch.None)
|
||||
{
|
||||
|
|
@ -46,12 +40,7 @@ namespace MapControl
|
|||
}
|
||||
}
|
||||
|
||||
// Path.MeasureOverride in Windows Runtime sometimes returns an empty Size,
|
||||
// whereas in Silverlight it occasionally throws an ArgumentException,
|
||||
// apparently because it tries to create a Size from negative width or height,
|
||||
// which result from a transformed Geometry.
|
||||
// In either case it seems to be sufficient to simply return a non-zero size.
|
||||
return new Size(1, 1);
|
||||
return new Size();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,14 +2,10 @@
|
|||
// © 2017 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
#if NETFX_CORE
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Media;
|
||||
#else
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
#endif
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
|
|
@ -19,11 +15,21 @@ namespace MapControl
|
|||
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;
|
||||
|
|
@ -2,8 +2,11 @@
|
|||
// © 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()
|
||||
87
MapControl/UWP/MatrixEx.UWP.cs
Normal file
87
MapControl/UWP/MatrixEx.UWP.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,14 +1,14 @@
|
|||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
[assembly: AssemblyTitle("XAML Map Control (WinRT)")]
|
||||
[assembly: AssemblyDescription("XAML Map Control Library for Windows Runtime")]
|
||||
[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("3.4.1")]
|
||||
[assembly: AssemblyFileVersion("3.4.1")]
|
||||
[assembly: AssemblyVersion("4.0.0")]
|
||||
[assembly: AssemblyFileVersion("4.0.0")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
[assembly: ComVisible(false)]
|
||||
5
MapControl/UWP/Properties/MapControl.UWP.rd.xml
Normal file
5
MapControl/UWP/Properties/MapControl.UWP.rd.xml
Normal 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>
|
||||
|
|
@ -2,11 +2,7 @@
|
|||
// © 2017 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
#if NETFX_CORE
|
||||
using Windows.UI.Xaml.Controls;
|
||||
#else
|
||||
using System.Windows.Controls;
|
||||
#endif
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
<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">
|
||||
|
|
@ -3,36 +3,28 @@
|
|||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using System;
|
||||
#if NETFX_CORE
|
||||
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;
|
||||
#else
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Animation;
|
||||
using System.Windows.Media.Imaging;
|
||||
#endif
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
public partial class Tile
|
||||
{
|
||||
public void SetImage(ImageSource image, bool fadeIn = true, bool isDownloading = true)
|
||||
public void SetImage(ImageSource imageSource, bool fadeIn = true)
|
||||
{
|
||||
Pending = false;
|
||||
|
||||
if (fadeIn && FadeDuration > TimeSpan.Zero)
|
||||
{
|
||||
BitmapImage bitmap;
|
||||
var bitmapImage = imageSource as BitmapImage;
|
||||
|
||||
if (isDownloading && (bitmap = image as BitmapImage) != null)
|
||||
if (bitmapImage != null && bitmapImage.UriSource != null)
|
||||
{
|
||||
bitmap.ImageOpened += BitmapImageOpened;
|
||||
bitmap.ImageFailed += BitmapImageFailed;
|
||||
bitmapImage.ImageOpened += BitmapImageOpened;
|
||||
bitmapImage.ImageFailed += BitmapImageFailed;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -45,15 +37,15 @@ namespace MapControl
|
|||
Image.Opacity = 1d;
|
||||
}
|
||||
|
||||
Image.Source = image;
|
||||
Image.Source = imageSource;
|
||||
}
|
||||
|
||||
private void BitmapImageOpened(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var bitmap = (BitmapImage)sender;
|
||||
var bitmapImage = (BitmapImage)sender;
|
||||
|
||||
bitmap.ImageOpened -= BitmapImageOpened;
|
||||
bitmap.ImageFailed -= BitmapImageFailed;
|
||||
bitmapImage.ImageOpened -= BitmapImageOpened;
|
||||
bitmapImage.ImageFailed -= BitmapImageFailed;
|
||||
|
||||
Image.BeginAnimation(UIElement.OpacityProperty,
|
||||
new DoubleAnimation { From = 0d, To = 1d, Duration = FadeDuration });
|
||||
|
|
@ -61,10 +53,10 @@ namespace MapControl
|
|||
|
||||
private void BitmapImageFailed(object sender, ExceptionRoutedEventArgs e)
|
||||
{
|
||||
var bitmap = (BitmapImage)sender;
|
||||
var bitmapImage = (BitmapImage)sender;
|
||||
|
||||
bitmap.ImageOpened -= BitmapImageOpened;
|
||||
bitmap.ImageFailed -= BitmapImageFailed;
|
||||
bitmapImage.ImageOpened -= BitmapImageOpened;
|
||||
bitmapImage.ImageFailed -= BitmapImageFailed;
|
||||
|
||||
Image.Source = null;
|
||||
}
|
||||
114
MapControl/UWP/TileImageLoader.UWP.cs
Normal file
114
MapControl/UWP/TileImageLoader.UWP.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
16
MapControl/UWP/project.json
Normal file
16
MapControl/UWP/project.json
Normal 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": {}
|
||||
}
|
||||
}
|
||||
274
MapControl/WPF/ImageFileCache.WPF.cs
Normal file
274
MapControl/WPF/ImageFileCache.WPF.cs
Normal file
|
|
@ -0,0 +1,274 @@
|
|||
// 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.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.Caching;
|
||||
using System.Security.AccessControl;
|
||||
using System.Security.Principal;
|
||||
|
||||
namespace MapControl.Caching
|
||||
{
|
||||
/// <summary>
|
||||
/// ObjectCache implementation based on local image files.
|
||||
/// The only valid data type for cached values is byte[].
|
||||
/// </summary>
|
||||
public class ImageFileCache : ObjectCache
|
||||
{
|
||||
private static readonly FileSystemAccessRule fullControlRule = new FileSystemAccessRule(
|
||||
new SecurityIdentifier(WellKnownSidType.BuiltinUsersSid, null),
|
||||
FileSystemRights.FullControl, AccessControlType.Allow);
|
||||
|
||||
private readonly MemoryCache memoryCache = MemoryCache.Default;
|
||||
private readonly string rootFolder;
|
||||
|
||||
public ImageFileCache(string rootFolder)
|
||||
{
|
||||
if (string.IsNullOrEmpty(rootFolder))
|
||||
{
|
||||
throw new ArgumentException("The parameter rootFolder must not be null or empty.");
|
||||
}
|
||||
|
||||
this.rootFolder = rootFolder;
|
||||
|
||||
Debug.WriteLine("Created ImageFileCache in " + rootFolder);
|
||||
}
|
||||
|
||||
public override string Name
|
||||
{
|
||||
get { return string.Empty; }
|
||||
}
|
||||
|
||||
public override DefaultCacheCapabilities DefaultCacheCapabilities
|
||||
{
|
||||
get { return DefaultCacheCapabilities.None; }
|
||||
}
|
||||
|
||||
public override object this[string key]
|
||||
{
|
||||
get { return Get(key); }
|
||||
set { Set(key, value, null); }
|
||||
}
|
||||
|
||||
protected override IEnumerator<KeyValuePair<string, object>> GetEnumerator()
|
||||
{
|
||||
throw new NotSupportedException("ImageFileCache does not support the ability to enumerate items.");
|
||||
}
|
||||
|
||||
public override CacheEntryChangeMonitor CreateCacheEntryChangeMonitor(IEnumerable<string> keys, string regionName = null)
|
||||
{
|
||||
throw new NotSupportedException("ImageFileCache does not support the ability to create change monitors.");
|
||||
}
|
||||
|
||||
public override long GetCount(string regionName = null)
|
||||
{
|
||||
throw new NotSupportedException("ImageFileCache does not support the ability to count items.");
|
||||
}
|
||||
|
||||
public override bool Contains(string key, string regionName = null)
|
||||
{
|
||||
if (key == null)
|
||||
{
|
||||
throw new ArgumentNullException("The parameter key must not be null.");
|
||||
}
|
||||
|
||||
if (regionName != null)
|
||||
{
|
||||
throw new NotSupportedException("The parameter regionName must be null.");
|
||||
}
|
||||
|
||||
return memoryCache.Contains(key) || FindFile(key) != null;
|
||||
}
|
||||
|
||||
public override object Get(string key, string regionName = null)
|
||||
{
|
||||
if (key == null)
|
||||
{
|
||||
throw new ArgumentNullException("The parameter key must not be null.");
|
||||
}
|
||||
|
||||
if (regionName != null)
|
||||
{
|
||||
throw new NotSupportedException("The parameter regionName must be null.");
|
||||
}
|
||||
|
||||
var buffer = memoryCache.Get(key) as byte[];
|
||||
|
||||
if (buffer == null)
|
||||
{
|
||||
var path = FindFile(key);
|
||||
|
||||
if (path != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
//Debug.WriteLine("ImageFileCache: Reading " + path);
|
||||
buffer = File.ReadAllBytes(path);
|
||||
memoryCache.Set(key, buffer, new CacheItemPolicy());
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine("ImageFileCache: Failed reading {0}: {1}", path, ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
public override CacheItem GetCacheItem(string key, string regionName = null)
|
||||
{
|
||||
var value = Get(key, regionName);
|
||||
|
||||
return value != null ? new CacheItem(key, value) : null;
|
||||
}
|
||||
|
||||
public override IDictionary<string, object> GetValues(IEnumerable<string> keys, string regionName = null)
|
||||
{
|
||||
return keys.ToDictionary(key => key, key => Get(key, regionName));
|
||||
}
|
||||
|
||||
public override void Set(string key, object value, CacheItemPolicy policy, string regionName = null)
|
||||
{
|
||||
if (key == null)
|
||||
{
|
||||
throw new ArgumentNullException("The parameter key must not be null.");
|
||||
}
|
||||
|
||||
if (regionName != null)
|
||||
{
|
||||
throw new NotSupportedException("The parameter regionName must be null.");
|
||||
}
|
||||
|
||||
var buffer = value as byte[];
|
||||
|
||||
if (buffer == null || buffer.Length == 0)
|
||||
{
|
||||
throw new NotSupportedException("The parameter value must be a non-empty byte array.");
|
||||
}
|
||||
|
||||
memoryCache.Set(key, buffer, policy);
|
||||
|
||||
var path = GetPath(key);
|
||||
|
||||
if (path != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
//Debug.WriteLine("ImageFileCache: Writing {0}, Expires {1}", path, policy.AbsoluteExpiration.DateTime.ToLocalTime());
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(path));
|
||||
File.WriteAllBytes(path, buffer);
|
||||
|
||||
var fileSecurity = File.GetAccessControl(path);
|
||||
fileSecurity.AddAccessRule(fullControlRule);
|
||||
File.SetAccessControl(path, fileSecurity);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine("ImageFileCache: Failed writing {0}: {1}", path, ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void Set(string key, object value, DateTimeOffset absoluteExpiration, string regionName = null)
|
||||
{
|
||||
Set(key, value, new CacheItemPolicy { AbsoluteExpiration = absoluteExpiration }, regionName);
|
||||
}
|
||||
|
||||
public override void Set(CacheItem item, CacheItemPolicy policy)
|
||||
{
|
||||
Set(item.Key, item.Value, policy, item.RegionName);
|
||||
}
|
||||
|
||||
public override object AddOrGetExisting(string key, object value, CacheItemPolicy policy, string regionName = null)
|
||||
{
|
||||
var oldValue = Get(key, regionName);
|
||||
|
||||
Set(key, value, policy);
|
||||
|
||||
return oldValue;
|
||||
}
|
||||
|
||||
public override object AddOrGetExisting(string key, object value, DateTimeOffset absoluteExpiration, string regionName = null)
|
||||
{
|
||||
return AddOrGetExisting(key, value, new CacheItemPolicy { AbsoluteExpiration = absoluteExpiration }, regionName);
|
||||
}
|
||||
|
||||
public override CacheItem AddOrGetExisting(CacheItem item, CacheItemPolicy policy)
|
||||
{
|
||||
var oldItem = GetCacheItem(item.Key, item.RegionName);
|
||||
|
||||
Set(item, policy);
|
||||
|
||||
return oldItem;
|
||||
}
|
||||
|
||||
public override object Remove(string key, string regionName = null)
|
||||
{
|
||||
if (key == null)
|
||||
{
|
||||
throw new ArgumentNullException("The parameter key must not be null.");
|
||||
}
|
||||
|
||||
if (regionName != null)
|
||||
{
|
||||
throw new NotSupportedException("The parameter regionName must be null.");
|
||||
}
|
||||
|
||||
memoryCache.Remove(key);
|
||||
|
||||
var path = FindFile(key);
|
||||
|
||||
if (path != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
File.Delete(path);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine("ImageFileCache: Failed removing {0}: {1}", path, ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private string FindFile(string key)
|
||||
{
|
||||
var path = GetPath(key);
|
||||
|
||||
try
|
||||
{
|
||||
if (path != null && File.Exists(path))
|
||||
{
|
||||
return path;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine("ImageFileCache: Failed finding {0}: {1}", path, ex.Message);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private string GetPath(string key)
|
||||
{
|
||||
try
|
||||
{
|
||||
return Path.Combine(rootFolder, Path.Combine(key.Split('\\', '/', ':', ';')));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine("ImageFileCache: Invalid key {0}/{1}: {2}", rootFolder, key, ex.Message);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
183
MapControl/WPF/MapControl.WPF.csproj
Normal file
183
MapControl/WPF/MapControl.WPF.csproj
Normal file
|
|
@ -0,0 +1,183 @@
|
|||
<?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>{A204A102-C745-4D65-AEC8-7B96FAEDEF2D}</ProjectGuid>
|
||||
<OutputType>library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>MapControl</RootNamespace>
|
||||
<AssemblyName>MapControl.WPF</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<TargetFrameworkProfile />
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>..\bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>none</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>..\bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<SignAssembly>true</SignAssembly>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<AssemblyOriginatorKeyFile>..\..\MapControl.snk</AssemblyOriginatorKeyFile>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Runtime.Caching" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="System.Xaml">
|
||||
<RequiredTargetFramework>4.0</RequiredTargetFramework>
|
||||
</Reference>
|
||||
<Reference Include="WindowsBase" />
|
||||
<Reference Include="PresentationCore" />
|
||||
<Reference Include="PresentationFramework" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Include="Themes\Generic.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
</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="ImageFileCache.WPF.cs" />
|
||||
<Compile Include="Map.WPF.cs" />
|
||||
<Compile Include="MapBase.WPF.cs" />
|
||||
<Compile Include="MapGraticule.WPF.cs" />
|
||||
<Compile Include="MapImageLayer.WPF.cs" />
|
||||
<Compile Include="MapItem.WPF.cs" />
|
||||
<Compile Include="MapItemsControl.WPF.cs" />
|
||||
<Compile Include="MapOverlay.WPF.cs" />
|
||||
<Compile Include="MapPanel.WPF.cs" />
|
||||
<Compile Include="MapPath.WPF.cs" />
|
||||
<Compile Include="MapPolyline.WPF.cs" />
|
||||
<Compile Include="MapScale.WPF.cs" />
|
||||
<Compile Include="MapTileLayer.WPF.cs" />
|
||||
<Compile Include="Pushpin.WPF.cs" />
|
||||
<Compile Include="TileImageLoader.WPF.cs" />
|
||||
<Compile Include="TypeConverters.WPF.cs" />
|
||||
<Compile Include="MatrixEx.WPF.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Tile.WPF.cs" />
|
||||
<Compile Include="XmlDocument.WPF.cs" />
|
||||
<None Include="..\..\MapControl.snk">
|
||||
<Link>MapControl.snk</Link>
|
||||
</None>
|
||||
<AppDesigner Include="Properties\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup />
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.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>
|
||||
57
MapControl/WPF/MapImageLayer.WPF.cs
Normal file
57
MapControl/WPF/MapImageLayer.WPF.cs
Normal 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 System.Windows.Controls;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
public partial class MapImageLayer
|
||||
{
|
||||
protected void UpdateImage(Uri uri)
|
||||
{
|
||||
UpdateImage(uri != null
|
||||
? BitmapFrame.Create(uri, BitmapCreateOptions.IgnoreImageCache, BitmapCacheOption.OnDemand)
|
||||
: null);
|
||||
}
|
||||
|
||||
protected void UpdateImage(BitmapSource bitmapSource)
|
||||
{
|
||||
SetTopImage(bitmapSource);
|
||||
|
||||
if (bitmapSource != null && !bitmapSource.IsFrozen && bitmapSource.IsDownloading)
|
||||
{
|
||||
bitmapSource.DownloadCompleted += BitmapDownloadCompleted;
|
||||
bitmapSource.DownloadFailed += BitmapDownloadFailed;
|
||||
}
|
||||
else
|
||||
{
|
||||
SwapImages();
|
||||
}
|
||||
}
|
||||
|
||||
private void BitmapDownloadCompleted(object sender, EventArgs e)
|
||||
{
|
||||
var bitmapSource = (BitmapSource)sender;
|
||||
|
||||
bitmapSource.DownloadCompleted -= BitmapDownloadCompleted;
|
||||
bitmapSource.DownloadFailed -= BitmapDownloadFailed;
|
||||
|
||||
SwapImages();
|
||||
}
|
||||
|
||||
private void BitmapDownloadFailed(object sender, ExceptionEventArgs e)
|
||||
{
|
||||
var bitmapSource = (BitmapSource)sender;
|
||||
|
||||
bitmapSource.DownloadCompleted -= BitmapDownloadCompleted;
|
||||
bitmapSource.DownloadFailed -= BitmapDownloadFailed;
|
||||
|
||||
((Image)Children[topImageIndex]).Source = null;
|
||||
SwapImages();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -31,10 +31,9 @@ namespace MapControl
|
|||
get { return Data; }
|
||||
}
|
||||
|
||||
protected override Size MeasureOverride(Size constraint)
|
||||
protected override Size MeasureOverride(Size availableSize)
|
||||
{
|
||||
// Shape.MeasureOverride sometimes returns an empty Size.
|
||||
return new Size(1, 1);
|
||||
return new Size();
|
||||
}
|
||||
|
||||
private static void DataPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
|
||||
|
|
@ -2,6 +2,8 @@
|
|||
// © 2017 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
|
|
@ -10,15 +12,26 @@ namespace MapControl
|
|||
{
|
||||
public partial class MapPolyline
|
||||
{
|
||||
public static readonly DependencyProperty FillRuleProperty = StreamGeometry.FillRuleProperty.AddOwner(
|
||||
typeof(MapPolyline),
|
||||
new FrameworkPropertyMetadata((o, e) => ((StreamGeometry)((MapPolyline)o).Data).FillRule = (FillRule)e.NewValue));
|
||||
public static readonly DependencyProperty FillRuleProperty = DependencyProperty.Register(
|
||||
nameof(FillRule), typeof(FillRule), typeof(MapPolyline), new FrameworkPropertyMetadata(
|
||||
FillRule.EvenOdd, FrameworkPropertyMetadataOptions.AffectsRender,
|
||||
(o, e) => ((StreamGeometry)((MapPolyline)o).Data).FillRule = (FillRule)e.NewValue));
|
||||
|
||||
public MapPolyline()
|
||||
{
|
||||
Data = new StreamGeometry();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the locations that define the polyline points.
|
||||
/// </summary>
|
||||
[TypeConverter(typeof(LocationCollectionConverter))]
|
||||
public IEnumerable<Location> Locations
|
||||
{
|
||||
get { return (IEnumerable<Location>)GetValue(LocationsProperty); }
|
||||
set { SetValue(LocationsProperty, value); }
|
||||
}
|
||||
|
||||
protected override void UpdateData()
|
||||
{
|
||||
var geometry = (StreamGeometry)Data;
|
||||
|
|
@ -3,9 +3,11 @@
|
|||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using System.Windows;
|
||||
using System.Windows.Markup;
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
[ContentProperty("TileSource")]
|
||||
public partial class MapTileLayer
|
||||
{
|
||||
static MapTileLayer()
|
||||
|
|
@ -16,7 +16,7 @@ namespace MapControl
|
|||
double scaleX, double scaleY, double rotationAngle,
|
||||
double translation2X, double translation2Y)
|
||||
{
|
||||
var matrix = new Matrix(1d, 0d, 0d, 1d, -translation1X, -translation1Y);
|
||||
var matrix = new Matrix(1d, 0d, 0d, 1d, translation1X, translation1Y);
|
||||
matrix.Scale(scaleX, scaleY);
|
||||
matrix.Rotate(rotationAngle);
|
||||
matrix.Translate(translation2X, translation2Y);
|
||||
|
|
@ -2,20 +2,15 @@
|
|||
using System.Runtime.InteropServices;
|
||||
using System.Windows;
|
||||
|
||||
#if SILVERLIGHT
|
||||
[assembly: AssemblyTitle("XAML Map Control (Silverlight)")]
|
||||
[assembly: AssemblyDescription("XAML Map Control Library for Silverlight")]
|
||||
#else
|
||||
[assembly: AssemblyTitle("XAML Map Control (WPF)")]
|
||||
[assembly: AssemblyTitle("XAML Map Control for WPF")]
|
||||
[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("© 2017 Clemens Fischer")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyVersion("3.4.1")]
|
||||
[assembly: AssemblyFileVersion("3.4.1")]
|
||||
[assembly: AssemblyVersion("4.0.0")]
|
||||
[assembly: AssemblyFileVersion("4.0.0")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
[assembly: ComVisible(false)]
|
||||
[assembly: ThemeInfo(ResourceDictionaryLocation.None, ResourceDictionaryLocation.SourceAssembly)]
|
||||
|
|
@ -13,30 +13,18 @@ namespace MapControl
|
|||
{
|
||||
public partial class Tile
|
||||
{
|
||||
public void SetImage(ImageSource image, bool fadeIn = true)
|
||||
public void SetImage(ImageSource imageSource, bool fadeIn = true)
|
||||
{
|
||||
Pending = false;
|
||||
|
||||
if (Image.Dispatcher.CheckAccess())
|
||||
{
|
||||
SetImageSource(image, fadeIn);
|
||||
}
|
||||
else
|
||||
{
|
||||
Image.Dispatcher.BeginInvoke(new Action(() => SetImageSource(image, fadeIn)));
|
||||
}
|
||||
}
|
||||
|
||||
private void SetImageSource(ImageSource image, bool fadeIn)
|
||||
{
|
||||
if (fadeIn && FadeDuration > TimeSpan.Zero)
|
||||
{
|
||||
var bitmap = image as BitmapSource;
|
||||
var bitmapSource = imageSource as BitmapSource;
|
||||
|
||||
if (bitmap != null && !bitmap.IsFrozen && bitmap.IsDownloading)
|
||||
if (bitmapSource != null && !bitmapSource.IsFrozen && bitmapSource.IsDownloading)
|
||||
{
|
||||
bitmap.DownloadCompleted += BitmapDownloadCompleted;
|
||||
bitmap.DownloadFailed += BitmapDownloadFailed;
|
||||
bitmapSource.DownloadCompleted += BitmapDownloadCompleted;
|
||||
bitmapSource.DownloadFailed += BitmapDownloadFailed;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -49,15 +37,15 @@ namespace MapControl
|
|||
Image.Opacity = 1d;
|
||||
}
|
||||
|
||||
Image.Source = image;
|
||||
Image.Source = imageSource;
|
||||
}
|
||||
|
||||
private void BitmapDownloadCompleted(object sender, EventArgs e)
|
||||
{
|
||||
var bitmap = (BitmapSource)sender;
|
||||
var bitmapSource = (BitmapSource)sender;
|
||||
|
||||
bitmap.DownloadCompleted -= BitmapDownloadCompleted;
|
||||
bitmap.DownloadFailed -= BitmapDownloadFailed;
|
||||
bitmapSource.DownloadCompleted -= BitmapDownloadCompleted;
|
||||
bitmapSource.DownloadFailed -= BitmapDownloadFailed;
|
||||
|
||||
Image.BeginAnimation(UIElement.OpacityProperty,
|
||||
new DoubleAnimation(0d, 1d, FadeDuration));
|
||||
|
|
@ -65,10 +53,10 @@ namespace MapControl
|
|||
|
||||
private void BitmapDownloadFailed(object sender, ExceptionEventArgs e)
|
||||
{
|
||||
var bitmap = (BitmapSource)sender;
|
||||
var bitmapSource = (BitmapSource)sender;
|
||||
|
||||
bitmap.DownloadCompleted -= BitmapDownloadCompleted;
|
||||
bitmap.DownloadFailed -= BitmapDownloadFailed;
|
||||
bitmapSource.DownloadCompleted -= BitmapDownloadCompleted;
|
||||
bitmapSource.DownloadFailed -= BitmapDownloadFailed;
|
||||
|
||||
Image.Source = null;
|
||||
}
|
||||
127
MapControl/WPF/TileImageLoader.WPF.cs
Normal file
127
MapControl/WPF/TileImageLoader.WPF.cs
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
// 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.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Runtime.Caching;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Media.Imaging;
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
public partial class TileImageLoader : ITileImageLoader
|
||||
{
|
||||
/// <summary>
|
||||
/// Default folder path where an ObjectCache instance may save cached data.
|
||||
/// </summary>
|
||||
public static readonly string DefaultCacheFolder =
|
||||
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "MapControl", "TileCache");
|
||||
|
||||
/// <summary>
|
||||
/// The ObjectCache used to cache tile images. The default is MemoryCache.Default.
|
||||
/// </summary>
|
||||
public static ObjectCache Cache { get; set; } = MemoryCache.Default;
|
||||
|
||||
private async Task LoadTileImageAsync(Tile tile, Uri uri, string cacheKey)
|
||||
{
|
||||
DateTime expiration;
|
||||
var buffer = GetCachedImage(cacheKey, out expiration);
|
||||
var loaded = false;
|
||||
|
||||
//if (buffer != null)
|
||||
//{
|
||||
// Debug.WriteLine("TileImageLoader: {0}: expire{1} {2}", cacheKey, expiration < DateTime.UtcNow ? "d" : "s", expiration);
|
||||
//}
|
||||
|
||||
if (buffer == null || expiration < DateTime.UtcNow)
|
||||
{
|
||||
loaded = await DownloadTileImageAsync(tile, uri, cacheKey);
|
||||
}
|
||||
|
||||
if (!loaded && buffer != null) // keep expired image if download failed
|
||||
{
|
||||
using (var stream = new MemoryStream(buffer))
|
||||
{
|
||||
await SetTileImageAsync(tile, stream);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<bool> DownloadTileImageAsync(Tile tile, Uri uri, string cacheKey)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var response = await HttpClient.GetAsync(uri))
|
||||
{
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
IEnumerable<string> tileInfo;
|
||||
|
||||
if (!response.Headers.TryGetValues(bingMapsTileInfo, out tileInfo) ||
|
||||
!tileInfo.Contains(bingMapsNoTile))
|
||||
{
|
||||
using (var stream = new MemoryStream())
|
||||
{
|
||||
await response.Content.CopyToAsync(stream);
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
await SetTileImageAsync(tile, stream); // create BitmapFrame in UI thread before caching
|
||||
|
||||
SetCachedImage(cacheKey, stream, 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, MemoryStream stream)
|
||||
{
|
||||
var imageSource = BitmapFrame.Create(stream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
|
||||
|
||||
await tile.Image.Dispatcher.InvokeAsync(() => tile.SetImage(imageSource));
|
||||
}
|
||||
|
||||
private static byte[] GetCachedImage(string cacheKey, out DateTime expiration)
|
||||
{
|
||||
var buffer = Cache.Get(cacheKey) as byte[];
|
||||
|
||||
if (buffer != null && buffer.Length >= 16 &&
|
||||
Encoding.ASCII.GetString(buffer, buffer.Length - 16, 8) == "EXPIRES:")
|
||||
{
|
||||
expiration = new DateTime(BitConverter.ToInt64(buffer, buffer.Length - 8), DateTimeKind.Utc);
|
||||
}
|
||||
else
|
||||
{
|
||||
expiration = DateTime.MinValue;
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
private static void SetCachedImage(string cacheKey, MemoryStream stream, DateTime expiration)
|
||||
{
|
||||
stream.Seek(0, SeekOrigin.End);
|
||||
stream.Write(Encoding.ASCII.GetBytes("EXPIRES:"), 0, 8);
|
||||
stream.Write(BitConverter.GetBytes(expiration.Ticks), 0, 8);
|
||||
|
||||
Cache.Set(cacheKey, stream.ToArray(), new CacheItemPolicy { AbsoluteExpiration = expiration });
|
||||
}
|
||||
}
|
||||
}
|
||||
84
MapControl/WPF/TypeConverters.WPF.cs
Normal file
84
MapControl/WPF/TypeConverters.WPF.cs
Normal 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.ComponentModel;
|
||||
using System.Globalization;
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
public class LocationConverter : TypeConverter
|
||||
{
|
||||
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
|
||||
{
|
||||
return sourceType == typeof(string);
|
||||
}
|
||||
|
||||
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
|
||||
{
|
||||
return Location.Parse((string)value);
|
||||
}
|
||||
}
|
||||
|
||||
[TypeConverter(typeof(LocationConverter))]
|
||||
[Serializable]
|
||||
public partial class Location
|
||||
{
|
||||
}
|
||||
|
||||
public class LocationCollectionConverter : TypeConverter
|
||||
{
|
||||
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
|
||||
{
|
||||
return sourceType == typeof(string);
|
||||
}
|
||||
|
||||
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
|
||||
{
|
||||
return LocationCollection.Parse((string)value);
|
||||
}
|
||||
}
|
||||
|
||||
[TypeConverter(typeof(LocationCollectionConverter))]
|
||||
public partial class LocationCollection
|
||||
{
|
||||
}
|
||||
|
||||
public class BoundingBoxConverter : TypeConverter
|
||||
{
|
||||
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
|
||||
{
|
||||
return sourceType == typeof(string);
|
||||
}
|
||||
|
||||
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
|
||||
{
|
||||
return BoundingBox.Parse((string)value);
|
||||
}
|
||||
}
|
||||
|
||||
[TypeConverter(typeof(BoundingBoxConverter))]
|
||||
[Serializable]
|
||||
public partial class BoundingBox
|
||||
{
|
||||
}
|
||||
|
||||
public class TileSourceConverter : TypeConverter
|
||||
{
|
||||
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
|
||||
{
|
||||
return sourceType == typeof(string);
|
||||
}
|
||||
|
||||
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
|
||||
{
|
||||
return new TileSource { UriFormat = value as string };
|
||||
}
|
||||
}
|
||||
|
||||
[TypeConverter(typeof(TileSourceConverter))]
|
||||
public partial class TileSource
|
||||
{
|
||||
}
|
||||
}
|
||||
24
MapControl/WPF/XmlDocument.WPF.cs
Normal file
24
MapControl/WPF/XmlDocument.WPF.cs
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
// 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;
|
||||
|
||||
#pragma warning disable CS1998 // async method lacks await operators and will run synchronously
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
public class XmlDocument : System.Xml.XmlDocument
|
||||
{
|
||||
public static Task<XmlDocument> LoadFromUriAsync(Uri uri)
|
||||
{
|
||||
return Task.Run(async () => // without async, debugger may stop on unhandled exception from XmlDocument.Load
|
||||
{
|
||||
var document = new XmlDocument();
|
||||
document.Load(uri.ToString());
|
||||
return document;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,226 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProductVersion>8.0.30703</ProductVersion>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<ProjectGuid>{63CEFDF7-5170-43B6-86F8-5C4A383A1615}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>MapControl</RootNamespace>
|
||||
<AssemblyName>MapControl.WinRT</AssemblyName>
|
||||
<DefaultLanguage>en-US</DefaultLanguage>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||
<MinimumVisualStudioVersion>12.0</MinimumVisualStudioVersion>
|
||||
<TargetFrameworkProfile>Profile32</TargetFrameworkProfile>
|
||||
<TargetFrameworkVersion>v4.6</TargetFrameworkVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>..\bin\Debug\</OutputPath>
|
||||
<DefineConstants>TRACE;DEBUG;NETFX_CORE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<GenerateLibraryLayout>true</GenerateLibraryLayout>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>none</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>..\bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE;NETFX_CORE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<GenerateLibraryLayout>true</GenerateLibraryLayout>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="..\AzimuthalEquidistantProjection.cs">
|
||||
<Link>AzimuthalEquidistantProjection.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\AzimuthalProjection.cs">
|
||||
<Link>AzimuthalProjection.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\BingMapsTileLayer.cs">
|
||||
<Link>BingMapsTileLayer.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\BingMapsTileSource.cs">
|
||||
<Link>BingMapsTileSource.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\BoundingBox.cs">
|
||||
<Link>BoundingBox.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\CenteredBoundingBox.cs">
|
||||
<Link>CenteredBoundingBox.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\EquirectangularProjection.cs">
|
||||
<Link>EquirectangularProjection.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Extensions.WinRT.cs">
|
||||
<Link>Extensions.WinRT.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\GnomonicProjection.cs">
|
||||
<Link>GnomonicProjection.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\HyperlinkText.cs">
|
||||
<Link>HyperlinkText.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\ImageCache.WinRT.cs">
|
||||
<Link>ImageCache.WinRT.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\ImageTileSource.Silverlight.WinRT.cs">
|
||||
<Link>ImageTileSource.Silverlight.WinRT.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Location.cs">
|
||||
<Link>Location.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\LocationCollection.cs">
|
||||
<Link>LocationCollection.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Map.WinRT.cs">
|
||||
<Link>Map.WinRT.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\MapBase.cs">
|
||||
<Link>MapBase.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\MapBase.Silverlight.WinRT.cs">
|
||||
<Link>MapBase.Silverlight.WinRT.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\MapGraticule.cs">
|
||||
<Link>MapGraticule.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\MapGraticule.Silverlight.WinRT.cs">
|
||||
<Link>MapGraticule.Silverlight.WinRT.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\MapImageLayer.cs">
|
||||
<Link>MapImageLayer.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\MapImageLayer.Silverlight.WinRT.cs">
|
||||
<Link>MapImageLayer.Silverlight.WinRT.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\MapItem.Silverlight.WinRT.cs">
|
||||
<Link>MapItem.Silverlight.WinRT.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\MapItemsControl.Silverlight.WinRT.cs">
|
||||
<Link>MapItemsControl.Silverlight.WinRT.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\MapOverlay.cs">
|
||||
<Link>MapOverlay.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\MapOverlay.Silverlight.WinRT.cs">
|
||||
<Link>MapOverlay.Silverlight.WinRT.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\MapPanel.cs">
|
||||
<Link>MapPanel.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\MapPanel.Silverlight.WinRT.cs">
|
||||
<Link>MapPanel.Silverlight.WinRT.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\MapPath.cs">
|
||||
<Link>MapPath.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\MapPath.Silverlight.WinRT.cs">
|
||||
<Link>MapPath.Silverlight.WinRT.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\MapPolyline.cs">
|
||||
<Link>MapPolyline.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\MapPolyline.Silverlight.WinRT.cs">
|
||||
<Link>MapPolyline.Silverlight.WinRT.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\MapProjection.cs">
|
||||
<Link>MapProjection.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\MapTileLayer.cs">
|
||||
<Link>MapTileLayer.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\MapTileLayer.Silverlight.WinRT.cs">
|
||||
<Link>MapTileLayer.Silverlight.WinRT.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\MatrixEx.Silverlight.WinRT.cs">
|
||||
<Link>MatrixEx.Silverlight.WinRT.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\OrthographicProjection.cs">
|
||||
<Link>OrthographicProjection.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Pushpin.Silverlight.WinRT.cs">
|
||||
<Link>Pushpin.Silverlight.WinRT.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\StereographicProjection.cs">
|
||||
<Link>StereographicProjection.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Tile.cs">
|
||||
<Link>Tile.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Tile.Silverlight.WinRT.cs">
|
||||
<Link>Tile.Silverlight.WinRT.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\TileGrid.cs">
|
||||
<Link>TileGrid.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\TileImageLoader.WinRT.cs">
|
||||
<Link>TileImageLoader.WinRT.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\TileSource.cs">
|
||||
<Link>TileSource.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\ViewportChangedEventArgs.cs">
|
||||
<Link>ViewportChangedEventArgs.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\WebMercatorProjection.cs">
|
||||
<Link>WebMercatorProjection.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\WmsImageLayer.cs">
|
||||
<Link>WmsImageLayer.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\WmsLayers.WPF.WinRT.cs">
|
||||
<Link>WmsLayers.WPF.WinRT.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="..\..\MapControl.snk">
|
||||
<Link>MapControl.snk</Link>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Include="Themes\Generic.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<TargetPlatform Include="Windows, Version=8.1" />
|
||||
<TargetPlatform Include="WindowsPhoneApp, Version=8.1" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup Condition=" '$(VisualStudioVersion)' == '' or '$(VisualStudioVersion)' < '12.0' ">
|
||||
<VisualStudioVersion>12.0</VisualStudioVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<RunPostBuildEvent>OnBuildSuccess</RunPostBuildEvent>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<SignAssembly>true</SignAssembly>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<AssemblyOriginatorKeyFile>..\..\MapControl.snk</AssemblyOriginatorKeyFile>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" />
|
||||
<PropertyGroup>
|
||||
<PreBuildEvent>
|
||||
</PreBuildEvent>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<PostBuildEvent>copy "$(ProjectDir)$(TargetName).xr.xml" "$(TargetDir)$(TargetName)\"</PostBuildEvent>
|
||||
</PropertyGroup>
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
||||
|
|
@ -1,61 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Roots>
|
||||
<Roots.RootTypes>
|
||||
<RootType FullName="Windows.UI.Xaml.ResourceDictionary" />
|
||||
<RootType FullName="MapControl.MapItemsControl" />
|
||||
<RootType FullName="Windows.UI.Xaml.Controls.Control">
|
||||
<RootProperty Name="Template" />
|
||||
<RootProperty Name="HorizontalContentAlignment" />
|
||||
<RootProperty Name="VerticalContentAlignment" />
|
||||
<RootProperty Name="Padding" />
|
||||
<RootProperty Name="Foreground" />
|
||||
<RootProperty Name="Background" />
|
||||
</RootType>
|
||||
<RootType FullName="Windows.UI.Xaml.Controls.ItemsControl">
|
||||
<RootProperty Name="ItemsPanel" />
|
||||
</RootType>
|
||||
<RootType FullName="MapControl.MapItem" />
|
||||
<RootType FullName="Windows.UI.Xaml.FrameworkElement">
|
||||
<RootProperty Name="HorizontalAlignment" />
|
||||
<RootProperty Name="VerticalAlignment" />
|
||||
</RootType>
|
||||
<RootType FullName="MapControl.Pushpin" />
|
||||
<RootType FullName="Windows.UI.Xaml.Setter">
|
||||
<RootProperty Name="Property" />
|
||||
<RootProperty Name="Value" />
|
||||
</RootType>
|
||||
<RootType FullName="Windows.UI.Xaml.Controls.ControlTemplate">
|
||||
<RootProperty Name="TargetType" />
|
||||
<RootProperty Name="Template" />
|
||||
</RootType>
|
||||
<RootType FullName="Windows.UI.Xaml.Controls.Grid">
|
||||
<RootProperty Name="RowDefinitions" />
|
||||
<RootProperty Name="Children" />
|
||||
<RootMethod Name="GetRow" />
|
||||
<RootMethod Name="SetRow" />
|
||||
</RootType>
|
||||
<RootType FullName="Windows.UI.Xaml.Controls.RowDefinition">
|
||||
<RootProperty Name="Height" />
|
||||
</RootType>
|
||||
<RootType FullName="Windows.UI.Xaml.Shapes.Rectangle">
|
||||
<RootProperty Name="Fill" />
|
||||
</RootType>
|
||||
<RootType FullName="Windows.UI.Xaml.Shapes.Path">
|
||||
<RootProperty Name="Fill" />
|
||||
<RootProperty Name="Data" />
|
||||
</RootType>
|
||||
<RootType FullName="Windows.UI.Xaml.Controls.ContentPresenter">
|
||||
<RootProperty Name="Content" />
|
||||
<RootProperty Name="ContentTemplate" />
|
||||
<RootProperty Name="Margin" />
|
||||
<RootProperty Name="HorizontalAlignment" />
|
||||
<RootProperty Name="VerticalAlignment" />
|
||||
</RootType>
|
||||
<RootType FullName="Microsoft.Xaml.Tools.DirectUI.ProxyTypes.TemplateBindingExtension" />
|
||||
<RootType FullName="Windows.UI.Xaml.Controls.ItemsPanelTemplate">
|
||||
<RootProperty Name="Template" />
|
||||
</RootType>
|
||||
<RootType FullName="MapControl.MapPanel" />
|
||||
<RootType FullName="Windows.UI.Xaml.Controls.ItemsPresenter" />
|
||||
</Roots.RootTypes>
|
||||
</Roots>
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
The Visual Studio Project for Windows Runtime resides in a separate folder
|
||||
MapControl/WinRT, because it needs to have its own Themes/Generic.xaml file.
|
||||
Output is generated to ../bin.
|
||||
|
||||
This folder also contains the file MapControl.WinRT.xr.xml, which overwrites
|
||||
the generated file in the output folder. This is to remove the XML namespace
|
||||
declaration on the <Roots> element, which prevents a .NET Native build of a
|
||||
Universal Windows App that uses the MapControl.WinRT portable library.
|
||||
|
|
@ -1,84 +0,0 @@
|
|||
// 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.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
#if NETFX_CORE
|
||||
using Windows.Data.Xml.Dom;
|
||||
#else
|
||||
using System.Xml;
|
||||
#endif
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
public static class WmsLayers
|
||||
{
|
||||
public static async Task<List<string>> GetAllLayers(Uri serverUri)
|
||||
{
|
||||
var allLayers = new List<string>();
|
||||
|
||||
if (serverUri != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
var uri = serverUri.ToString();
|
||||
|
||||
if (!uri.EndsWith("?") && !uri.EndsWith("&"))
|
||||
{
|
||||
uri += "?";
|
||||
}
|
||||
|
||||
var document = await LoadDocument(new Uri(uri + "SERVICE=WMS&VERSION=1.3.0&REQUEST=GetCapabilities"));
|
||||
|
||||
var capability = FirstChild(document.DocumentElement, "Capability");
|
||||
if (capability != null)
|
||||
{
|
||||
var rootLayer = FirstChild(capability, "Layer");
|
||||
if (rootLayer != null)
|
||||
{
|
||||
foreach (var layer in ChildElements(rootLayer, "Layer"))
|
||||
{
|
||||
var name = FirstChild(layer, "Name");
|
||||
if (name != null)
|
||||
{
|
||||
allLayers.Add(name.InnerText);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine(ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
return allLayers;
|
||||
}
|
||||
|
||||
private static IEnumerable<XmlElement> ChildElements(XmlElement element, string name)
|
||||
{
|
||||
return element.ChildNodes.OfType<XmlElement>().Where(e => (string)e.LocalName == name);
|
||||
}
|
||||
|
||||
private static XmlElement FirstChild(XmlElement element, string name)
|
||||
{
|
||||
return element.ChildNodes.OfType<XmlElement>().FirstOrDefault(e => (string)e.LocalName == name);
|
||||
}
|
||||
|
||||
private static async Task<XmlDocument> LoadDocument(Uri requestUri)
|
||||
{
|
||||
#if NETFX_CORE
|
||||
return await XmlDocument.LoadFromUriAsync(requestUri);
|
||||
#else
|
||||
var document = new XmlDocument();
|
||||
await Task.Run(() => document.Load(requestUri.ToString()));
|
||||
return document;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue