Version 4.10.0: Updated target framework versions. Cleanup of TypeConverters, ImageLoader, MBTileSource.

This commit is contained in:
ClemensF 2018-08-08 23:31:52 +02:00
parent 63a4c1f0a7
commit 6a1653056f
36 changed files with 272 additions and 292 deletions

View file

@ -46,7 +46,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform"> <PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform">
<Version>6.1.5</Version> <Version>6.1.7</Version>
</PackageReference> </PackageReference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View file

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

View file

@ -9,7 +9,7 @@
<AppDesignerFolder>Properties</AppDesignerFolder> <AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>MapControl.Caching</RootNamespace> <RootNamespace>MapControl.Caching</RootNamespace>
<AssemblyName>FileDbCache.WPF</AssemblyName> <AssemblyName>FileDbCache.WPF</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion> <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment> <FileAlignment>512</FileAlignment>
<TargetFrameworkProfile /> <TargetFrameworkProfile />
</PropertyGroup> </PropertyGroup>

View file

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

View file

@ -1,3 +1,7 @@
// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2018 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL)
#if WINDOWS_UWP #if WINDOWS_UWP
using Windows.UI.Xaml; using Windows.UI.Xaml;
#else #else

View file

@ -1,4 +1,8 @@
using System; // XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2018 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL)
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Runtime.InteropServices.WindowsRuntime; using System.Runtime.InteropServices.WindowsRuntime;
@ -6,7 +10,6 @@ using System.Threading.Tasks;
using Microsoft.Data.Sqlite; using Microsoft.Data.Sqlite;
using Windows.Storage.Streams; using Windows.Storage.Streams;
using Windows.UI.Xaml.Media; using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Media.Imaging;
namespace MapControl namespace MapControl
{ {
@ -53,10 +56,7 @@ namespace MapControl
await stream.WriteAsync(buffer.AsBuffer()); await stream.WriteAsync(buffer.AsBuffer());
stream.Seek(0); stream.Seek(0);
var bitmapImage = new BitmapImage(); imageSource = await ImageLoader.CreateImageSourceAsync(stream);
await bitmapImage.SetSourceAsync(stream);
imageSource = bitmapImage;
} }
} }
} }

View file

@ -52,7 +52,7 @@
<Version>2.1.0</Version> <Version>2.1.0</Version>
</PackageReference> </PackageReference>
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform"> <PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform">
<Version>6.1.5</Version> <Version>6.1.7</Version>
</PackageReference> </PackageReference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View file

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

View file

@ -1,11 +1,14 @@
using System; // XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2018 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL)
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Data.SQLite;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Data.SQLite;
using System.Windows.Media; using System.Windows.Media;
using System.Windows.Media.Imaging;
namespace MapControl namespace MapControl
{ {
@ -46,14 +49,11 @@ namespace MapControl
var buffer = await command.ExecuteScalarAsync() as byte[]; var buffer = await command.ExecuteScalarAsync() as byte[];
if (buffer != null) if (buffer != null)
{
imageSource = await Task.Run(() =>
{ {
using (var stream = new MemoryStream(buffer)) using (var stream = new MemoryStream(buffer))
{ {
return BitmapFrame.Create(stream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad); imageSource = await ImageLoader.CreateImageSourceAsync(stream);
} }
});
} }
} }
} }

View file

@ -9,7 +9,7 @@
<AppDesignerFolder>Properties</AppDesignerFolder> <AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>MapControl</RootNamespace> <RootNamespace>MapControl</RootNamespace>
<AssemblyName>MBTiles.WPF</AssemblyName> <AssemblyName>MBTiles.WPF</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion> <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment> <FileAlignment>512</FileAlignment>
<TargetFrameworkProfile /> <TargetFrameworkProfile />
<NuGetPackageImportStamp> <NuGetPackageImportStamp>
@ -38,8 +38,8 @@
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Core" /> <Reference Include="System.Core" />
<Reference Include="System.Data" /> <Reference Include="System.Data" />
<Reference Include="System.Data.SQLite, Version=1.0.107.0, Culture=neutral, PublicKeyToken=db937bc2d44ff139, processorArchitecture=MSIL"> <Reference Include="System.Data.SQLite, Version=1.0.108.0, Culture=neutral, PublicKeyToken=db937bc2d44ff139, processorArchitecture=MSIL">
<HintPath>..\..\packages\System.Data.SQLite.Core.1.0.107.0\lib\net45\System.Data.SQLite.dll</HintPath> <HintPath>..\..\packages\System.Data.SQLite.Core.1.0.108.0\lib\net46\System.Data.SQLite.dll</HintPath>
</Reference> </Reference>
<Reference Include="System.Xaml" /> <Reference Include="System.Xaml" />
<Reference Include="WindowsBase" /> <Reference Include="WindowsBase" />
@ -61,11 +61,11 @@
<None Include="packages.config" /> <None Include="packages.config" />
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="..\..\packages\System.Data.SQLite.Core.1.0.107.0\build\net45\System.Data.SQLite.Core.targets" Condition="Exists('..\..\packages\System.Data.SQLite.Core.1.0.107.0\build\net45\System.Data.SQLite.Core.targets')" /> <Import Project="..\..\packages\System.Data.SQLite.Core.1.0.108.0\build\net46\System.Data.SQLite.Core.targets" Condition="Exists('..\..\packages\System.Data.SQLite.Core.1.0.108.0\build\net46\System.Data.SQLite.Core.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild"> <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup> <PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText> <ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup> </PropertyGroup>
<Error Condition="!Exists('..\..\packages\System.Data.SQLite.Core.1.0.107.0\build\net45\System.Data.SQLite.Core.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\System.Data.SQLite.Core.1.0.107.0\build\net45\System.Data.SQLite.Core.targets'))" /> <Error Condition="!Exists('..\..\packages\System.Data.SQLite.Core.1.0.108.0\build\net46\System.Data.SQLite.Core.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\System.Data.SQLite.Core.1.0.108.0\build\net46\System.Data.SQLite.Core.targets'))" />
</Target> </Target>
</Project> </Project>

View file

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

View file

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<packages> <packages>
<package id="System.Data.SQLite.Core" version="1.0.107.0" targetFramework="net45" /> <package id="System.Data.SQLite.Core" version="1.0.108.0" targetFramework="net47" />
</packages> </packages>

View file

@ -10,7 +10,10 @@ namespace MapControl
/// <summary> /// <summary>
/// A geographic bounding box with south and north latitude and west and east longitude values in degrees. /// A geographic bounding box with south and north latitude and west and east longitude values in degrees.
/// </summary> /// </summary>
public partial class BoundingBox #if !WINDOWS_UWP
[System.ComponentModel.TypeConverter(typeof(BoundingBoxConverter))]
#endif
public class BoundingBox
{ {
private double south; private double south;
private double west; private double west;

View file

@ -0,0 +1,69 @@
// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2018 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL)
using System;
using System.Diagnostics;
using System.Threading.Tasks;
#if WINDOWS_UWP
using Windows.Web.Http;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Media.Imaging;
#else
using System.Net.Http;
using System.Windows.Media;
using System.Windows.Media.Imaging;
#endif
namespace MapControl
{
public static partial class ImageLoader
{
/// <summary>
/// The HttpClient instance used when image data is downloaded from a web resource.
/// </summary>
public static HttpClient HttpClient { get; set; } = new HttpClient();
public static async Task<ImageSource> LoadImageAsync(Uri uri, bool isTileImage)
{
ImageSource imageSource = null;
if (!uri.IsAbsoluteUri || uri.Scheme == "file")
{
imageSource = await LoadLocalImageAsync(uri);
}
else if (uri.Scheme == "http")
{
imageSource = await LoadHttpImageAsync(uri, isTileImage);
}
else
{
imageSource = new BitmapImage(uri);
}
return imageSource;
}
public static async Task<ImageSource> LoadHttpImageAsync(Uri uri, bool isTileImage)
{
ImageSource imageSource = null;
using (var response = await HttpClient.GetAsync(uri))
{
if (!response.IsSuccessStatusCode)
{
Debug.WriteLine("ImageLoader: {0}: {1} {2}", uri, (int)response.StatusCode, response.ReasonPhrase);
}
else if (!isTileImage || IsTileAvailable(response.Headers))
{
using (var stream = await GetResponseStreamAsync(response.Content))
{
imageSource = await CreateImageSourceAsync(stream);
}
}
return imageSource;
}
}
}
}

View file

@ -10,7 +10,10 @@ namespace MapControl
/// <summary> /// <summary>
/// A geographic location with latitude and longitude values in degrees. /// A geographic location with latitude and longitude values in degrees.
/// </summary> /// </summary>
public partial class Location : IEquatable<Location> #if !WINDOWS_UWP
[System.ComponentModel.TypeConverter(typeof(LocationConverter))]
#endif
public class Location : IEquatable<Location>
{ {
private double latitude; private double latitude;
private double longitude; private double longitude;

View file

@ -11,7 +11,10 @@ namespace MapControl
/// <summary> /// <summary>
/// A collection of Locations with support for parsing. /// A collection of Locations with support for parsing.
/// </summary> /// </summary>
public partial class LocationCollection : List<Location> #if !WINDOWS_UWP
[System.ComponentModel.TypeConverter(typeof(LocationCollectionConverter))]
#endif
public class LocationCollection : List<Location>
{ {
public LocationCollection() public LocationCollection()
{ {

View file

@ -7,6 +7,9 @@ using System.Linq;
namespace MapControl namespace MapControl
{ {
/// <summary>
/// Provides helper methods for geodetic calculations on a sphere.
/// </summary>
public static class LocationEx public static class LocationEx
{ {
/// <summary> /// <summary>
@ -18,9 +21,48 @@ namespace MapControl
var lon1 = location1.Longitude * Math.PI / 180d; var lon1 = location1.Longitude * Math.PI / 180d;
var lat2 = location2.Latitude * Math.PI / 180d; var lat2 = location2.Latitude * Math.PI / 180d;
var lon2 = location2.Longitude * Math.PI / 180d; var lon2 = location2.Longitude * Math.PI / 180d;
var cosS12 = Math.Sin(lat1) * Math.Sin(lat2) + Math.Cos(lat1) * Math.Cos(lat2) * Math.Cos(lon2 - lon1); var sinLat1 = Math.Sin(lat1);
var cosLat1 = Math.Cos(lat1);
var sinLat2 = Math.Sin(lat2);
var cosLat2 = Math.Cos(lat2);
var cosLon12 = Math.Cos(lon2 - lon1);
var cosS12 = sinLat1 * sinLat2 + cosLat1 * cosLat2 * cosLon12;
var s12 = 0d;
return earthRadius * Math.Acos(Math.Min(Math.Max(cosS12, -1d), 1d)); if (Math.Abs(cosS12) < 0.99999999)
{
s12 = Math.Acos(Math.Min(Math.Max(cosS12, -1d), 1d));
}
else
{
var sinLon12 = Math.Sin(lon2 - lon1);
var a = cosLat1 * sinLat2 - sinLat1 * cosLat2 * cosLon12;
var b = cosLat2 * sinLon12;
s12 = Math.Atan2(Math.Sqrt(a * a + b * b), cosS12);
}
return earthRadius * s12;
}
/// <summary>
/// see https://en.wikipedia.org/wiki/Great-circle_navigation
/// </summary>
public static Location GreatCircleLocation(this Location location, double azimuth, double distance, double earthRadius = MapProjection.Wgs84EquatorialRadius)
{
var s12 = distance / earthRadius;
var az1 = azimuth * Math.PI / 180d;
var lat1 = location.Latitude * Math.PI / 180d;
var lon1 = location.Longitude * Math.PI / 180d;
var sinS12 = Math.Sin(s12);
var cosS12 = Math.Cos(s12);
var sinAz1 = Math.Sin(az1);
var cosAz1 = Math.Cos(az1);
var sinLat1 = Math.Sin(lat1);
var cosLat1 = Math.Cos(lat1);
var lat2 = Math.Asin(sinLat1 * cosS12 + cosLat1 * sinS12 * cosAz1);
var lon2 = lon1 + Math.Atan2(sinS12 * sinAz1, (cosLat1 * cosS12 - sinLat1 * sinS12 * cosAz1));
return new Location(lat2 / Math.PI * 180d, lon2 / Math.PI * 180d);
} }
public static LocationCollection CalculateMeridianLocations(this Location location, double latitude2, double resolution = 1d) public static LocationCollection CalculateMeridianLocations(this Location location, double latitude2, double resolution = 1d)

View file

@ -344,9 +344,11 @@ namespace MapControl
var maxZoomLevel = Math.Min(TileGrid.ZoomLevel, MaxZoomLevel); var maxZoomLevel = Math.Min(TileGrid.ZoomLevel, MaxZoomLevel);
var minZoomLevel = MinZoomLevel; var minZoomLevel = MinZoomLevel;
if (minZoomLevel < maxZoomLevel && parentMap.MapLayer != this) // load lower tiles only in a base layer if (minZoomLevel < maxZoomLevel &&
parentMap.MapLayer != this &&
parentMap.Children.Cast<UIElement>().FirstOrDefault() != this)
{ {
minZoomLevel = maxZoomLevel; minZoomLevel = maxZoomLevel; // do not load lower level tiles if this is note a "base" layer
} }
for (var z = minZoomLevel; z <= maxZoomLevel; z++) for (var z = minZoomLevel; z <= maxZoomLevel; z++)

View file

@ -9,6 +9,7 @@ using System.Threading.Tasks;
#if WINDOWS_UWP #if WINDOWS_UWP
using Windows.UI.Xaml.Media; using Windows.UI.Xaml.Media;
#else #else
using System.ComponentModel;
using System.Windows.Media; using System.Windows.Media;
#endif #endif
@ -17,7 +18,10 @@ namespace MapControl
/// <summary> /// <summary>
/// Provides the download Uri or ImageSource of map tiles. /// Provides the download Uri or ImageSource of map tiles.
/// </summary> /// </summary>
public partial class TileSource #if !WINDOWS_UWP
[TypeConverter(typeof(TileSourceConverter))]
#endif
public class TileSource
{ {
private Func<int, int, int, string> getUri; private Func<int, int, int, string> getUri;
private string uriFormat; private string uriFormat;

View file

@ -15,72 +15,24 @@ using Windows.Web.Http.Headers;
namespace MapControl namespace MapControl
{ {
public static class ImageLoader public static partial class ImageLoader
{ {
/// <summary>
/// The HttpClient instance used when image data is downloaded from a web resource.
/// </summary>
public static HttpClient HttpClient { get; set; } = new HttpClient();
public static async Task<ImageSource> LoadImageAsync(Uri uri, bool isTileImage)
{
if (!uri.IsAbsoluteUri || uri.Scheme == "file")
{
return await LoadLocalImageAsync(uri);
}
if (uri.Scheme == "http")
{
return await LoadHttpImageAsync(uri, isTileImage);
}
return new BitmapImage(uri);
}
public static async Task<ImageSource> LoadLocalImageAsync(Uri uri) public static async Task<ImageSource> LoadLocalImageAsync(Uri uri)
{ {
ImageSource imageSource = null;
var path = uri.IsAbsoluteUri ? uri.LocalPath : uri.OriginalString; var path = uri.IsAbsoluteUri ? uri.LocalPath : uri.OriginalString;
if (!File.Exists(path)) if (File.Exists(path))
{ {
return null;
}
var file = await StorageFile.GetFileFromPathAsync(path); var file = await StorageFile.GetFileFromPathAsync(path);
using (var stream = await file.OpenReadAsync()) using (var stream = await file.OpenReadAsync())
{ {
var bitmapImage = new BitmapImage(); imageSource = await CreateImageSourceAsync(stream);
await bitmapImage.SetSourceAsync(stream);
return bitmapImage;
} }
} }
public static async Task<ImageSource> LoadHttpImageAsync(Uri uri, bool isTileImage) return imageSource;
{
using (var response = await HttpClient.GetAsync(uri))
{
if (!response.IsSuccessStatusCode)
{
Debug.WriteLine("ImageLoader: {0}: {1} {2}", uri, (int)response.StatusCode, response.ReasonPhrase);
}
else if (!isTileImage || IsTileAvailable(response.Headers))
{
using (var stream = new InMemoryRandomAccessStream())
{
await response.Content.WriteToStreamAsync(stream);
stream.Seek(0);
var bitmapImage = new BitmapImage();
await bitmapImage.SetSourceAsync(stream);
return bitmapImage;
}
}
return null;
}
} }
public static async Task<bool> LoadHttpTileImageAsync(Uri uri, Func<IBuffer, TimeSpan?, Task> tileCallback) public static async Task<bool> LoadHttpTileImageAsync(Uri uri, Func<IBuffer, TimeSpan?, Task> tileCallback)
@ -102,6 +54,21 @@ namespace MapControl
} }
} }
public static async Task<ImageSource> CreateImageSourceAsync(IRandomAccessStream stream)
{
var bitmapImage = new BitmapImage();
await bitmapImage.SetSourceAsync(stream);
return bitmapImage;
}
private static async Task<InMemoryRandomAccessStream> GetResponseStreamAsync(IHttpContent content)
{
var stream = new InMemoryRandomAccessStream();
await content.WriteToStreamAsync(stream);
stream.Seek(0);
return stream;
}
private static bool IsTileAvailable(HttpResponseHeaderCollection responseHeaders) private static bool IsTileAvailable(HttpResponseHeaderCollection responseHeaders)
{ {
return !responseHeaders.TryGetValue("X-VE-Tile-Info", out string tileInfo) || tileInfo != "no-tile"; return !responseHeaders.TryGetValue("X-VE-Tile-Info", out string tileInfo) || tileInfo != "no-tile";

View file

@ -67,6 +67,9 @@
<Compile Include="..\Shared\HyperlinkText.cs"> <Compile Include="..\Shared\HyperlinkText.cs">
<Link>HyperlinkText.cs</Link> <Link>HyperlinkText.cs</Link>
</Compile> </Compile>
<Compile Include="..\Shared\ImageLoader.cs">
<Link>ImageLoader.cs</Link>
</Compile>
<Compile Include="..\Shared\Intersections.cs"> <Compile Include="..\Shared\Intersections.cs">
<Link>Intersections.cs</Link> <Link>Intersections.cs</Link>
</Compile> </Compile>
@ -168,7 +171,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform"> <PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform">
<Version>6.1.5</Version> <Version>6.1.7</Version>
</PackageReference> </PackageReference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View file

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

View file

@ -8,11 +8,10 @@ using System.Threading.Tasks;
using Windows.Storage; using Windows.Storage;
using Windows.Storage.Streams; using Windows.Storage.Streams;
using Windows.UI.Core; using Windows.UI.Core;
using Windows.UI.Xaml.Media.Imaging;
namespace MapControl namespace MapControl
{ {
public partial class TileImageLoader : ITileImageLoader public partial class TileImageLoader
{ {
/// <summary> /// <summary>
/// Default StorageFolder where an IImageCache instance may save cached data, /// Default StorageFolder where an IImageCache instance may save cached data,
@ -43,10 +42,10 @@ namespace MapControl
{ {
try try
{ {
loaded = await ImageLoader.LoadHttpTileImageAsync(uri, async (buffer, maxAge) => loaded = await ImageLoader.LoadHttpTileImageAsync(uri,
async (buffer, maxAge) =>
{ {
await SetTileImageAsync(tile, buffer); // create BitmapImage before caching await SetTileImageAsync(tile, buffer); // create BitmapImage before caching
await Cache.SetAsync(cacheKey, buffer, GetExpiration(maxAge)); await Cache.SetAsync(cacheKey, buffer, GetExpiration(maxAge));
}); });
} }
@ -76,10 +75,7 @@ namespace MapControl
{ {
try try
{ {
var bitmapImage = new BitmapImage(); tile.SetImage(await ImageLoader.CreateImageSourceAsync(stream));
await bitmapImage.SetSourceAsync(stream);
tile.SetImage(bitmapImage);
tcs.SetResult(null); tcs.SetResult(null);
} }
catch (Exception ex) catch (Exception ex)

View file

@ -1,81 +0,0 @@
// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2018 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;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Media.Imaging;
using Windows.Web.Http;
using Windows.Web.Http.Headers;
namespace MapControl
{
public partial class TileSource
{
/// <summary>
/// The HttpClient instance used when image data is downloaded from a web resource.
/// </summary>
public static HttpClient HttpClient { get; set; } = new HttpClient();
/// <summary>
/// Check HTTP response headers for tile availability, e.g. X-VE-Tile-Info=no-tile
/// </summary>
public static bool IsTileAvailable(HttpResponseHeaderCollection responseHeaders)
{
string tileInfo;
return !responseHeaders.TryGetValue("X-VE-Tile-Info", out tileInfo) || tileInfo != "no-tile";
}
protected static async Task<ImageSource> LoadLocalImageAsync(Uri uri)
{
var path = uri.IsAbsoluteUri ? uri.LocalPath : uri.OriginalString;
if (!await Task.Run(() => File.Exists(path)))
{
return null;
}
var file = await StorageFile.GetFileFromPathAsync(path);
using (var stream = await file.OpenReadAsync())
{
var bitmapImage = new BitmapImage();
await bitmapImage.SetSourceAsync(stream);
return bitmapImage;
}
}
protected static async Task<ImageSource> LoadHttpImageAsync(Uri uri)
{
using (var response = await HttpClient.GetAsync(uri))
{
if (!response.IsSuccessStatusCode)
{
Debug.WriteLine("TileSource: {0}: {1} {2}", uri, (int)response.StatusCode, response.ReasonPhrase);
}
else if (IsTileAvailable(response.Headers))
{
using (var stream = new InMemoryRandomAccessStream())
{
await response.Content.WriteToStreamAsync(stream);
stream.Seek(0);
var bitmapImage = new BitmapImage();
await bitmapImage.SetSourceAsync(stream);
return bitmapImage;
}
}
}
return null;
}
}
}

View file

@ -15,69 +15,27 @@ using System.Windows.Media.Imaging;
namespace MapControl namespace MapControl
{ {
public static class ImageLoader public static partial class ImageLoader
{ {
/// <summary>
/// The HttpClient instance used when image data is downloaded from a web resource.
/// </summary>
public static HttpClient HttpClient { get; set; } = new HttpClient();
public static async Task<ImageSource> LoadImageAsync(Uri uri, bool isTileImage)
{
if (!uri.IsAbsoluteUri || uri.Scheme == "file")
{
return await LoadLocalImageAsync(uri);
}
if (uri.Scheme == "http")
{
return await LoadHttpImageAsync(uri, isTileImage);
}
return new BitmapImage(uri);
}
public static Task<ImageSource> LoadLocalImageAsync(Uri uri) public static Task<ImageSource> LoadLocalImageAsync(Uri uri)
{ {
return Task.Run(() => return Task.Run(() =>
{ {
ImageSource imageSource = null;
var path = uri.IsAbsoluteUri ? uri.LocalPath : uri.OriginalString; var path = uri.IsAbsoluteUri ? uri.LocalPath : uri.OriginalString;
if (!File.Exists(path)) if (File.Exists(path))
{ {
return null; using (var stream = File.OpenRead(path))
{
imageSource = CreateImageSource(stream);
}
} }
using (var stream = new FileStream(path, FileMode.Open, FileAccess.Read)) return imageSource;
{
return (ImageSource)BitmapFrame.Create(stream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
}
}); });
} }
public static async Task<ImageSource> LoadHttpImageAsync(Uri uri, bool isTileImage)
{
using (var response = await HttpClient.GetAsync(uri))
{
if (!response.IsSuccessStatusCode)
{
Debug.WriteLine("ImageLoader: {0}: {1} {2}", uri, (int)response.StatusCode, response.ReasonPhrase);
}
else if (!isTileImage || IsTileAvailable(response.Headers))
{
using (var stream = new MemoryStream())
{
await response.Content.CopyToAsync(stream);
stream.Seek(0, SeekOrigin.Begin);
return BitmapFrame.Create(stream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
}
}
return null;
}
}
public static async Task<bool> LoadHttpTileImageAsync(Uri uri, Func<MemoryStream, TimeSpan?, Task> tileCallback) public static async Task<bool> LoadHttpTileImageAsync(Uri uri, Func<MemoryStream, TimeSpan?, Task> tileCallback)
{ {
using (var response = await HttpClient.GetAsync(uri)) using (var response = await HttpClient.GetAsync(uri))
@ -88,22 +46,46 @@ namespace MapControl
} }
else if (IsTileAvailable(response.Headers)) else if (IsTileAvailable(response.Headers))
{ {
var stream = new MemoryStream(); using (var stream = new MemoryStream())
{
await response.Content.CopyToAsync(stream); await response.Content.CopyToAsync(stream);
stream.Seek(0, SeekOrigin.Begin); stream.Seek(0, SeekOrigin.Begin);
await tileCallback(stream, response.Headers.CacheControl?.MaxAge); await tileCallback(stream, response.Headers.CacheControl?.MaxAge);
} }
}
return response.IsSuccessStatusCode; return response.IsSuccessStatusCode;
} }
} }
public static ImageSource CreateImageSource(Stream stream)
{
var bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.StreamSource = stream;
bitmapImage.EndInit();
bitmapImage.Freeze();
return bitmapImage;
}
public static Task<ImageSource> CreateImageSourceAsync(Stream stream)
{
return Task.Run(() => CreateImageSource(stream));
}
private static async Task<Stream> GetResponseStreamAsync(HttpContent content)
{
var stream = new MemoryStream();
await content.CopyToAsync(stream);
stream.Seek(0, SeekOrigin.Begin);
return stream;
}
private static bool IsTileAvailable(HttpResponseHeaders responseHeaders) private static bool IsTileAvailable(HttpResponseHeaders responseHeaders)
{ {
IEnumerable<string> tileInfo; IEnumerable<string> tileInfo;
return !responseHeaders.TryGetValues("X-VE-Tile-Info", out tileInfo) || !tileInfo.Contains("no-tile"); return !responseHeaders.TryGetValues("X-VE-Tile-Info", out tileInfo) || !tileInfo.Contains("no-tile");
} }
} }

View file

@ -9,11 +9,12 @@
<AppDesignerFolder>Properties</AppDesignerFolder> <AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>MapControl</RootNamespace> <RootNamespace>MapControl</RootNamespace>
<AssemblyName>MapControl.WPF</AssemblyName> <AssemblyName>MapControl.WPF</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion> <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment> <FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids> <ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
<TargetFrameworkProfile /> <TargetFrameworkProfile>
</TargetFrameworkProfile>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols> <DebugSymbols>true</DebugSymbols>
@ -25,6 +26,7 @@
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
<NoWarn> <NoWarn>
</NoWarn> </NoWarn>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>none</DebugType> <DebugType>none</DebugType>
@ -33,6 +35,7 @@
<DefineConstants>TRACE</DefineConstants> <DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <PropertyGroup>
<SignAssembly>true</SignAssembly> <SignAssembly>true</SignAssembly>
@ -86,6 +89,9 @@
<Compile Include="..\Shared\HyperlinkText.cs"> <Compile Include="..\Shared\HyperlinkText.cs">
<Link>HyperlinkText.cs</Link> <Link>HyperlinkText.cs</Link>
</Compile> </Compile>
<Compile Include="..\Shared\ImageLoader.cs">
<Link>ImageLoader.cs</Link>
</Compile>
<Compile Include="..\Shared\Intersections.cs"> <Compile Include="..\Shared\Intersections.cs">
<Link>Intersections.cs</Link> <Link>Intersections.cs</Link>
</Compile> </Compile>

View file

@ -72,7 +72,7 @@ namespace MapControl
{ {
latLabels.Add(new Label(lat, new FormattedText( latLabels.Add(new Label(lat, new FormattedText(
GetLabelText(lat, labelFormat, "NS"), GetLabelText(lat, labelFormat, "NS"),
CultureInfo.InvariantCulture, FlowDirection.LeftToRight, typeface, FontSize, Foreground))); CultureInfo.InvariantCulture, FlowDirection.LeftToRight, typeface, FontSize, Foreground, 1d)));
drawingContext.DrawLine(pen, drawingContext.DrawLine(pen,
projection.LocationToViewportPoint(new Location(lat, boundingBox.West)), projection.LocationToViewportPoint(new Location(lat, boundingBox.West)),
@ -83,7 +83,7 @@ namespace MapControl
{ {
lonLabels.Add(new Label(lon, new FormattedText( lonLabels.Add(new Label(lon, new FormattedText(
GetLabelText(Location.NormalizeLongitude(lon), labelFormat, "EW"), GetLabelText(Location.NormalizeLongitude(lon), labelFormat, "EW"),
CultureInfo.InvariantCulture, FlowDirection.LeftToRight, typeface, FontSize, Foreground))); CultureInfo.InvariantCulture, FlowDirection.LeftToRight, typeface, FontSize, Foreground, 1d)));
drawingContext.DrawLine(pen, drawingContext.DrawLine(pen,
projection.LocationToViewportPoint(new Location(boundingBox.South, lon)), projection.LocationToViewportPoint(new Location(boundingBox.South, lon)),

View file

@ -8,8 +8,8 @@ using System.Windows;
[assembly: AssemblyCompany("Clemens Fischer")] [assembly: AssemblyCompany("Clemens Fischer")]
[assembly: AssemblyCopyright("© 2018 Clemens Fischer")] [assembly: AssemblyCopyright("© 2018 Clemens Fischer")]
[assembly: AssemblyTrademark("")] [assembly: AssemblyTrademark("")]
[assembly: AssemblyVersion("4.9.0")] [assembly: AssemblyVersion("4.10.0")]
[assembly: AssemblyFileVersion("4.9.0")] [assembly: AssemblyFileVersion("4.10.0")]
[assembly: AssemblyConfiguration("")] [assembly: AssemblyConfiguration("")]
[assembly: AssemblyCulture("")] [assembly: AssemblyCulture("")]
[assembly: ComVisible(false)] [assembly: ComVisible(false)]

View file

@ -9,11 +9,10 @@ using System.Net;
using System.Runtime.Caching; using System.Runtime.Caching;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows.Media.Imaging;
namespace MapControl namespace MapControl
{ {
public partial class TileImageLoader : ITileImageLoader public partial class TileImageLoader
{ {
/// <summary> /// <summary>
/// Default folder path where an ObjectCache instance may save cached data, /// Default folder path where an ObjectCache instance may save cached data,
@ -44,10 +43,10 @@ namespace MapControl
{ {
try try
{ {
loaded = await ImageLoader.LoadHttpTileImageAsync(uri, async (stream, maxAge) => loaded = await ImageLoader.LoadHttpTileImageAsync(uri,
async (stream, maxAge) =>
{ {
await SetTileImageAsync(tile, stream); // create BitmapFrame before caching await SetTileImageAsync(tile, stream); // create BitmapImage before caching
SetCachedImage(cacheKey, stream, GetExpiration(maxAge)); SetCachedImage(cacheKey, stream, GetExpiration(maxAge));
}); });
} }
@ -68,7 +67,7 @@ namespace MapControl
private async Task SetTileImageAsync(Tile tile, MemoryStream stream) private async Task SetTileImageAsync(Tile tile, MemoryStream stream)
{ {
var imageSource = BitmapFrame.Create(stream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad); var imageSource = ImageLoader.CreateImageSource(stream);
await tile.Image.Dispatcher.InvokeAsync(() => tile.SetImage(imageSource)); await tile.Image.Dispatcher.InvokeAsync(() => tile.SetImage(imageSource));
} }

View file

@ -21,12 +21,6 @@ namespace MapControl
} }
} }
[TypeConverter(typeof(LocationConverter))]
[Serializable]
public partial class Location
{
}
public class LocationCollectionConverter : TypeConverter public class LocationCollectionConverter : TypeConverter
{ {
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
@ -40,11 +34,6 @@ namespace MapControl
} }
} }
[TypeConverter(typeof(LocationCollectionConverter))]
public partial class LocationCollection
{
}
public class BoundingBoxConverter : TypeConverter public class BoundingBoxConverter : TypeConverter
{ {
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
@ -58,12 +47,6 @@ namespace MapControl
} }
} }
[TypeConverter(typeof(BoundingBoxConverter))]
[Serializable]
public partial class BoundingBox
{
}
public class TileSourceConverter : TypeConverter public class TileSourceConverter : TypeConverter
{ {
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
@ -76,9 +59,4 @@ namespace MapControl
return new TileSource { UriFormat = value as string }; return new TileSource { UriFormat = value as string };
} }
} }
[TypeConverter(typeof(TileSourceConverter))]
public partial class TileSource
{
}
} }

View file

@ -8,7 +8,7 @@ using System.Runtime.InteropServices;
[assembly: AssemblyCopyright("© 2018 Clemens Fischer")] [assembly: AssemblyCopyright("© 2018 Clemens Fischer")]
[assembly: AssemblyTrademark("")] [assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")] [assembly: AssemblyCulture("")]
[assembly: AssemblyVersion("4.9.0")] [assembly: AssemblyVersion("4.10.0")]
[assembly: AssemblyFileVersion("4.9.0")] [assembly: AssemblyFileVersion("4.10.0")]
[assembly: AssemblyConfiguration("")] [assembly: AssemblyConfiguration("")]
[assembly: ComVisible(false)] [assembly: ComVisible(false)]

View file

@ -156,7 +156,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform"> <PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform">
<Version>6.1.5</Version> <Version>6.1.7</Version>
</PackageReference> </PackageReference>
</ItemGroup> </ItemGroup>
<PropertyGroup Condition=" '$(VisualStudioVersion)' == '' or '$(VisualStudioVersion)' &lt; '14.0' "> <PropertyGroup Condition=" '$(VisualStudioVersion)' == '' or '$(VisualStudioVersion)' &lt; '14.0' ">

View file

@ -1,6 +1,6 @@
<?xml version="1.0"?> <?xml version="1.0"?>
<configuration> <configuration>
<startup> <startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5"/> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2"/>
</startup> </startup>
</configuration> </configuration>

View file

@ -134,7 +134,7 @@ namespace WpfApplication
advanceWidths[i] = glyphTypeface.AdvanceWidths[glyphIndex] * FontSize; advanceWidths[i] = glyphTypeface.AdvanceWidths[glyphIndex] * FontSize;
} }
glyphRun = new GlyphRun(glyphTypeface, 0, false, FontSize, glyphIndices, new Point(), advanceWidths, null, null, null, null, null, null); glyphRun = new GlyphRun(glyphTypeface, 0, false, FontSize, 1f, glyphIndices, new Point(), advanceWidths, null, null, null, null, null, null);
outline = glyphRun.BuildGeometry().GetWidenedPathGeometry(new Pen(null, OutlineThickness * 2d)); outline = glyphRun.BuildGeometry().GetWidenedPathGeometry(new Pen(null, OutlineThickness * 2d));
} }

View file

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

View file

@ -9,7 +9,7 @@
<AppDesignerFolder>Properties</AppDesignerFolder> <AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>WpfApplication</RootNamespace> <RootNamespace>WpfApplication</RootNamespace>
<AssemblyName>WpfApplication</AssemblyName> <AssemblyName>WpfApplication</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion> <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment> <FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids> <ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>