Version 3.1.Added support for different map projections.

This commit is contained in:
ClemensF 2017-06-25 23:05:48 +02:00
parent 06c3ed56c1
commit 643abeca1e
124 changed files with 3074 additions and 2497 deletions

View file

@ -1,5 +1,5 @@
// XAML Map Control - http://xamlmapcontrol.codeplex.com/ // XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2016 Clemens Fischer // © 2017 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL) // Licensed under the Microsoft Public License (Ms-PL)
using System; using System;

View file

@ -5,10 +5,10 @@ using System.Runtime.InteropServices;
[assembly: AssemblyDescription("ObjectCache implementation based on EzTools FileDb")] [assembly: AssemblyDescription("ObjectCache implementation based on EzTools FileDb")]
[assembly: AssemblyProduct("XAML Map Control")] [assembly: AssemblyProduct("XAML Map Control")]
[assembly: AssemblyCompany("Clemens Fischer")] [assembly: AssemblyCompany("Clemens Fischer")]
[assembly: AssemblyCopyright("© 2016 Clemens Fischer")] [assembly: AssemblyCopyright("© 2017 Clemens Fischer")]
[assembly: AssemblyTrademark("")] [assembly: AssemblyTrademark("")]
[assembly: AssemblyVersion("2.14.0")] [assembly: AssemblyVersion("3.1.0")]
[assembly: AssemblyFileVersion("2.14.0")] [assembly: AssemblyFileVersion("3.1.0")]
[assembly: AssemblyConfiguration("")] [assembly: AssemblyConfiguration("")]
[assembly: AssemblyCulture("")] [assembly: AssemblyCulture("")]
[assembly: ComVisible(false)] [assembly: ComVisible(false)]

View file

@ -1,5 +1,5 @@
// XAML Map Control - http://xamlmapcontrol.codeplex.com/ // XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2016 Clemens Fischer // © 2017 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL) // Licensed under the Microsoft Public License (Ms-PL)
using System; using System;

View file

@ -5,10 +5,10 @@ using System.Runtime.InteropServices;
[assembly: AssemblyDescription("IImageCache implementation based on EzTools FileDb")] [assembly: AssemblyDescription("IImageCache implementation based on EzTools FileDb")]
[assembly: AssemblyProduct("XAML Map Control")] [assembly: AssemblyProduct("XAML Map Control")]
[assembly: AssemblyCompany("Clemens Fischer")] [assembly: AssemblyCompany("Clemens Fischer")]
[assembly: AssemblyCopyright("© 2016 Clemens Fischer")] [assembly: AssemblyCopyright("© 2017 Clemens Fischer")]
[assembly: AssemblyTrademark("")] [assembly: AssemblyTrademark("")]
[assembly: AssemblyVersion("2.14.0")] [assembly: AssemblyVersion("3.1.0")]
[assembly: AssemblyFileVersion("2.14.0")] [assembly: AssemblyFileVersion("3.1.0")]
[assembly: AssemblyConfiguration("")] [assembly: AssemblyConfiguration("")]
[assembly: AssemblyCulture("")] [assembly: AssemblyCulture("")]
[assembly: ComVisible(false)] [assembly: ComVisible(false)]

View file

@ -1,5 +1,5 @@
// XAML Map Control - http://xamlmapcontrol.codeplex.com/ // XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2016 Clemens Fischer // © 2017 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL) // Licensed under the Microsoft Public License (Ms-PL)
using System; using System;

View file

@ -5,10 +5,10 @@ using System.Runtime.InteropServices;
[assembly: AssemblyDescription("ObjectCache implementation based on local image files")] [assembly: AssemblyDescription("ObjectCache implementation based on local image files")]
[assembly: AssemblyProduct("XAML Map Control")] [assembly: AssemblyProduct("XAML Map Control")]
[assembly: AssemblyCompany("Clemens Fischer")] [assembly: AssemblyCompany("Clemens Fischer")]
[assembly: AssemblyCopyright("© 2016 Clemens Fischer")] [assembly: AssemblyCopyright("© 2017 Clemens Fischer")]
[assembly: AssemblyTrademark("")] [assembly: AssemblyTrademark("")]
[assembly: AssemblyVersion("2.14.0")] [assembly: AssemblyVersion("3.1.0")]
[assembly: AssemblyFileVersion("2.14.0")] [assembly: AssemblyFileVersion("3.1.0")]
[assembly: AssemblyConfiguration("")] [assembly: AssemblyConfiguration("")]
[assembly: AssemblyCulture("")] [assembly: AssemblyCulture("")]
[assembly: ComVisible(false)] [assembly: ComVisible(false)]

View file

@ -1,5 +1,5 @@
// XAML Map Control - http://xamlmapcontrol.codeplex.com/ // XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2016 Clemens Fischer // © 2017 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL) // Licensed under the Microsoft Public License (Ms-PL)
using System; using System;

View file

@ -5,10 +5,10 @@ using System.Runtime.InteropServices;
[assembly: AssemblyDescription("IImageCache implementation based on local image files")] [assembly: AssemblyDescription("IImageCache implementation based on local image files")]
[assembly: AssemblyProduct("XAML Map Control")] [assembly: AssemblyProduct("XAML Map Control")]
[assembly: AssemblyCompany("Clemens Fischer")] [assembly: AssemblyCompany("Clemens Fischer")]
[assembly: AssemblyCopyright("© 2016 Clemens Fischer")] [assembly: AssemblyCopyright("© 2017 Clemens Fischer")]
[assembly: AssemblyTrademark("")] [assembly: AssemblyTrademark("")]
[assembly: AssemblyVersion("2.14.0")] [assembly: AssemblyVersion("3.1.0")]
[assembly: AssemblyFileVersion("2.14.0")] [assembly: AssemblyFileVersion("3.1.0")]
[assembly: AssemblyConfiguration("")] [assembly: AssemblyConfiguration("")]
[assembly: AssemblyCulture("")] [assembly: AssemblyCulture("")]
[assembly: ComVisible(false)] [assembly: ComVisible(false)]

View file

@ -1,238 +0,0 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.25420.1
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FileDbCache.WinRT", "Caching\FileDbCache.WinRT\FileDbCache.WinRT.csproj", "{C7BF2B18-CC74-430B-BCB2-600304EFA3D8}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FileDbCache.WPF", "Caching\FileDbCache.WPF\FileDbCache.WPF.csproj", "{EF44F661-B98A-4676-927F-85D138F82300}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ImageFileCache.WinRT", "Caching\ImageFileCache.WinRT\ImageFileCache.WinRT.csproj", "{F789647E-96F7-43E3-A895-FA3FE8D01260}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ImageFileCache.WPF", "Caching\ImageFileCache.WPF\ImageFileCache.WPF.csproj", "{86470440-FEE2-4120-AF5A-3762FB9C536F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MapControl.Silverlight", "MapControl\MapControl.Silverlight.csproj", "{EB133B78-DEFF-416A-8F0C-89E54D766576}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MapControl.WinRT", "MapControl\WinRT\MapControl.WinRT.csproj", "{63CEFDF7-5170-43B6-86F8-5C4A383A1615}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MapControl.WPF", "MapControl\MapControl.WPF.csproj", "{226F3575-B683-446D-A2F0-181291DC8787}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SilverlightApplication", "SampleApps\SilverlightApplication\SilverlightApplication.csproj", "{CBA8C535-CCA3-4F60-8D3E-0E25791CBD21}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SilverlightApplication.Web", "SampleApps\SilverlightApplication.Web\SilverlightApplication.Web.csproj", "{177C4EF8-0B0A-426E-BDCC-168DC10AC1C1}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WpfApplication", "SampleApps\WpfApplication\WpfApplication.csproj", "{9949326E-9261-4F95-89B1-151F60498951}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SampleApps", "SampleApps", "{100879CC-8910-459E-856E-253D629E45DE}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Caching", "Caching", "{AE8A7E02-0F7D-41B0-AB23-15394150ED17}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UniversalApp", "SampleApps\UniversalApp\UniversalApp.csproj", "{AA62B4AA-1CA3-4C20-BEB7-B824D0FC4BD1}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|ARM = Debug|ARM
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|ARM = Release|ARM
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{C7BF2B18-CC74-430B-BCB2-600304EFA3D8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C7BF2B18-CC74-430B-BCB2-600304EFA3D8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C7BF2B18-CC74-430B-BCB2-600304EFA3D8}.Debug|ARM.ActiveCfg = Debug|Any CPU
{C7BF2B18-CC74-430B-BCB2-600304EFA3D8}.Debug|ARM.Build.0 = Debug|Any CPU
{C7BF2B18-CC74-430B-BCB2-600304EFA3D8}.Debug|x64.ActiveCfg = Debug|Any CPU
{C7BF2B18-CC74-430B-BCB2-600304EFA3D8}.Debug|x64.Build.0 = Debug|Any CPU
{C7BF2B18-CC74-430B-BCB2-600304EFA3D8}.Debug|x86.ActiveCfg = Debug|Any CPU
{C7BF2B18-CC74-430B-BCB2-600304EFA3D8}.Debug|x86.Build.0 = Debug|Any CPU
{C7BF2B18-CC74-430B-BCB2-600304EFA3D8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C7BF2B18-CC74-430B-BCB2-600304EFA3D8}.Release|Any CPU.Build.0 = Release|Any CPU
{C7BF2B18-CC74-430B-BCB2-600304EFA3D8}.Release|ARM.ActiveCfg = Release|Any CPU
{C7BF2B18-CC74-430B-BCB2-600304EFA3D8}.Release|ARM.Build.0 = Release|Any CPU
{C7BF2B18-CC74-430B-BCB2-600304EFA3D8}.Release|x64.ActiveCfg = Release|Any CPU
{C7BF2B18-CC74-430B-BCB2-600304EFA3D8}.Release|x64.Build.0 = Release|Any CPU
{C7BF2B18-CC74-430B-BCB2-600304EFA3D8}.Release|x86.ActiveCfg = Release|Any CPU
{C7BF2B18-CC74-430B-BCB2-600304EFA3D8}.Release|x86.Build.0 = Release|Any CPU
{EF44F661-B98A-4676-927F-85D138F82300}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EF44F661-B98A-4676-927F-85D138F82300}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EF44F661-B98A-4676-927F-85D138F82300}.Debug|ARM.ActiveCfg = Debug|Any CPU
{EF44F661-B98A-4676-927F-85D138F82300}.Debug|ARM.Build.0 = Debug|Any CPU
{EF44F661-B98A-4676-927F-85D138F82300}.Debug|x64.ActiveCfg = Debug|Any CPU
{EF44F661-B98A-4676-927F-85D138F82300}.Debug|x64.Build.0 = Debug|Any CPU
{EF44F661-B98A-4676-927F-85D138F82300}.Debug|x86.ActiveCfg = Debug|Any CPU
{EF44F661-B98A-4676-927F-85D138F82300}.Debug|x86.Build.0 = Debug|Any CPU
{EF44F661-B98A-4676-927F-85D138F82300}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EF44F661-B98A-4676-927F-85D138F82300}.Release|Any CPU.Build.0 = Release|Any CPU
{EF44F661-B98A-4676-927F-85D138F82300}.Release|ARM.ActiveCfg = Release|Any CPU
{EF44F661-B98A-4676-927F-85D138F82300}.Release|ARM.Build.0 = Release|Any CPU
{EF44F661-B98A-4676-927F-85D138F82300}.Release|x64.ActiveCfg = Release|Any CPU
{EF44F661-B98A-4676-927F-85D138F82300}.Release|x64.Build.0 = Release|Any CPU
{EF44F661-B98A-4676-927F-85D138F82300}.Release|x86.ActiveCfg = Release|Any CPU
{EF44F661-B98A-4676-927F-85D138F82300}.Release|x86.Build.0 = Release|Any CPU
{F789647E-96F7-43E3-A895-FA3FE8D01260}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F789647E-96F7-43E3-A895-FA3FE8D01260}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F789647E-96F7-43E3-A895-FA3FE8D01260}.Debug|ARM.ActiveCfg = Debug|Any CPU
{F789647E-96F7-43E3-A895-FA3FE8D01260}.Debug|ARM.Build.0 = Debug|Any CPU
{F789647E-96F7-43E3-A895-FA3FE8D01260}.Debug|x64.ActiveCfg = Debug|Any CPU
{F789647E-96F7-43E3-A895-FA3FE8D01260}.Debug|x64.Build.0 = Debug|Any CPU
{F789647E-96F7-43E3-A895-FA3FE8D01260}.Debug|x86.ActiveCfg = Debug|Any CPU
{F789647E-96F7-43E3-A895-FA3FE8D01260}.Debug|x86.Build.0 = Debug|Any CPU
{F789647E-96F7-43E3-A895-FA3FE8D01260}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F789647E-96F7-43E3-A895-FA3FE8D01260}.Release|Any CPU.Build.0 = Release|Any CPU
{F789647E-96F7-43E3-A895-FA3FE8D01260}.Release|ARM.ActiveCfg = Release|Any CPU
{F789647E-96F7-43E3-A895-FA3FE8D01260}.Release|ARM.Build.0 = Release|Any CPU
{F789647E-96F7-43E3-A895-FA3FE8D01260}.Release|x64.ActiveCfg = Release|Any CPU
{F789647E-96F7-43E3-A895-FA3FE8D01260}.Release|x64.Build.0 = Release|Any CPU
{F789647E-96F7-43E3-A895-FA3FE8D01260}.Release|x86.ActiveCfg = Release|Any CPU
{F789647E-96F7-43E3-A895-FA3FE8D01260}.Release|x86.Build.0 = Release|Any CPU
{86470440-FEE2-4120-AF5A-3762FB9C536F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{86470440-FEE2-4120-AF5A-3762FB9C536F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{86470440-FEE2-4120-AF5A-3762FB9C536F}.Debug|ARM.ActiveCfg = Debug|Any CPU
{86470440-FEE2-4120-AF5A-3762FB9C536F}.Debug|ARM.Build.0 = Debug|Any CPU
{86470440-FEE2-4120-AF5A-3762FB9C536F}.Debug|x64.ActiveCfg = Debug|Any CPU
{86470440-FEE2-4120-AF5A-3762FB9C536F}.Debug|x64.Build.0 = Debug|Any CPU
{86470440-FEE2-4120-AF5A-3762FB9C536F}.Debug|x86.ActiveCfg = Debug|Any CPU
{86470440-FEE2-4120-AF5A-3762FB9C536F}.Debug|x86.Build.0 = Debug|Any CPU
{86470440-FEE2-4120-AF5A-3762FB9C536F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{86470440-FEE2-4120-AF5A-3762FB9C536F}.Release|Any CPU.Build.0 = Release|Any CPU
{86470440-FEE2-4120-AF5A-3762FB9C536F}.Release|ARM.ActiveCfg = Release|Any CPU
{86470440-FEE2-4120-AF5A-3762FB9C536F}.Release|ARM.Build.0 = Release|Any CPU
{86470440-FEE2-4120-AF5A-3762FB9C536F}.Release|x64.ActiveCfg = Release|Any CPU
{86470440-FEE2-4120-AF5A-3762FB9C536F}.Release|x64.Build.0 = Release|Any CPU
{86470440-FEE2-4120-AF5A-3762FB9C536F}.Release|x86.ActiveCfg = Release|Any CPU
{86470440-FEE2-4120-AF5A-3762FB9C536F}.Release|x86.Build.0 = Release|Any CPU
{EB133B78-DEFF-416A-8F0C-89E54D766576}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EB133B78-DEFF-416A-8F0C-89E54D766576}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EB133B78-DEFF-416A-8F0C-89E54D766576}.Debug|ARM.ActiveCfg = Debug|Any CPU
{EB133B78-DEFF-416A-8F0C-89E54D766576}.Debug|ARM.Build.0 = Debug|Any CPU
{EB133B78-DEFF-416A-8F0C-89E54D766576}.Debug|x64.ActiveCfg = Debug|Any CPU
{EB133B78-DEFF-416A-8F0C-89E54D766576}.Debug|x64.Build.0 = Debug|Any CPU
{EB133B78-DEFF-416A-8F0C-89E54D766576}.Debug|x86.ActiveCfg = Debug|Any CPU
{EB133B78-DEFF-416A-8F0C-89E54D766576}.Debug|x86.Build.0 = Debug|Any CPU
{EB133B78-DEFF-416A-8F0C-89E54D766576}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EB133B78-DEFF-416A-8F0C-89E54D766576}.Release|Any CPU.Build.0 = Release|Any CPU
{EB133B78-DEFF-416A-8F0C-89E54D766576}.Release|ARM.ActiveCfg = Release|Any CPU
{EB133B78-DEFF-416A-8F0C-89E54D766576}.Release|ARM.Build.0 = Release|Any CPU
{EB133B78-DEFF-416A-8F0C-89E54D766576}.Release|x64.ActiveCfg = Release|Any CPU
{EB133B78-DEFF-416A-8F0C-89E54D766576}.Release|x64.Build.0 = Release|Any CPU
{EB133B78-DEFF-416A-8F0C-89E54D766576}.Release|x86.ActiveCfg = Release|Any CPU
{EB133B78-DEFF-416A-8F0C-89E54D766576}.Release|x86.Build.0 = Release|Any CPU
{63CEFDF7-5170-43B6-86F8-5C4A383A1615}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{63CEFDF7-5170-43B6-86F8-5C4A383A1615}.Debug|Any CPU.Build.0 = Debug|Any CPU
{63CEFDF7-5170-43B6-86F8-5C4A383A1615}.Debug|ARM.ActiveCfg = Debug|Any CPU
{63CEFDF7-5170-43B6-86F8-5C4A383A1615}.Debug|ARM.Build.0 = Debug|Any CPU
{63CEFDF7-5170-43B6-86F8-5C4A383A1615}.Debug|x64.ActiveCfg = Debug|Any CPU
{63CEFDF7-5170-43B6-86F8-5C4A383A1615}.Debug|x64.Build.0 = Debug|Any CPU
{63CEFDF7-5170-43B6-86F8-5C4A383A1615}.Debug|x86.ActiveCfg = Debug|Any CPU
{63CEFDF7-5170-43B6-86F8-5C4A383A1615}.Debug|x86.Build.0 = Debug|Any CPU
{63CEFDF7-5170-43B6-86F8-5C4A383A1615}.Release|Any CPU.ActiveCfg = Release|Any CPU
{63CEFDF7-5170-43B6-86F8-5C4A383A1615}.Release|Any CPU.Build.0 = Release|Any CPU
{63CEFDF7-5170-43B6-86F8-5C4A383A1615}.Release|ARM.ActiveCfg = Release|Any CPU
{63CEFDF7-5170-43B6-86F8-5C4A383A1615}.Release|ARM.Build.0 = Release|Any CPU
{63CEFDF7-5170-43B6-86F8-5C4A383A1615}.Release|x64.ActiveCfg = Release|Any CPU
{63CEFDF7-5170-43B6-86F8-5C4A383A1615}.Release|x64.Build.0 = Release|Any CPU
{63CEFDF7-5170-43B6-86F8-5C4A383A1615}.Release|x86.ActiveCfg = Release|Any CPU
{63CEFDF7-5170-43B6-86F8-5C4A383A1615}.Release|x86.Build.0 = Release|Any CPU
{226F3575-B683-446D-A2F0-181291DC8787}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{226F3575-B683-446D-A2F0-181291DC8787}.Debug|Any CPU.Build.0 = Debug|Any CPU
{226F3575-B683-446D-A2F0-181291DC8787}.Debug|ARM.ActiveCfg = Debug|Any CPU
{226F3575-B683-446D-A2F0-181291DC8787}.Debug|ARM.Build.0 = Debug|Any CPU
{226F3575-B683-446D-A2F0-181291DC8787}.Debug|x64.ActiveCfg = Debug|Any CPU
{226F3575-B683-446D-A2F0-181291DC8787}.Debug|x64.Build.0 = Debug|Any CPU
{226F3575-B683-446D-A2F0-181291DC8787}.Debug|x86.ActiveCfg = Debug|Any CPU
{226F3575-B683-446D-A2F0-181291DC8787}.Debug|x86.Build.0 = Debug|Any CPU
{226F3575-B683-446D-A2F0-181291DC8787}.Release|Any CPU.ActiveCfg = Release|Any CPU
{226F3575-B683-446D-A2F0-181291DC8787}.Release|Any CPU.Build.0 = Release|Any CPU
{226F3575-B683-446D-A2F0-181291DC8787}.Release|ARM.ActiveCfg = Release|Any CPU
{226F3575-B683-446D-A2F0-181291DC8787}.Release|ARM.Build.0 = Release|Any CPU
{226F3575-B683-446D-A2F0-181291DC8787}.Release|x64.ActiveCfg = Release|Any CPU
{226F3575-B683-446D-A2F0-181291DC8787}.Release|x64.Build.0 = Release|Any CPU
{226F3575-B683-446D-A2F0-181291DC8787}.Release|x86.ActiveCfg = Release|Any CPU
{226F3575-B683-446D-A2F0-181291DC8787}.Release|x86.Build.0 = Release|Any CPU
{CBA8C535-CCA3-4F60-8D3E-0E25791CBD21}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CBA8C535-CCA3-4F60-8D3E-0E25791CBD21}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CBA8C535-CCA3-4F60-8D3E-0E25791CBD21}.Debug|ARM.ActiveCfg = Debug|Any CPU
{CBA8C535-CCA3-4F60-8D3E-0E25791CBD21}.Debug|ARM.Build.0 = Debug|Any CPU
{CBA8C535-CCA3-4F60-8D3E-0E25791CBD21}.Debug|x64.ActiveCfg = Debug|Any CPU
{CBA8C535-CCA3-4F60-8D3E-0E25791CBD21}.Debug|x64.Build.0 = Debug|Any CPU
{CBA8C535-CCA3-4F60-8D3E-0E25791CBD21}.Debug|x86.ActiveCfg = Debug|Any CPU
{CBA8C535-CCA3-4F60-8D3E-0E25791CBD21}.Debug|x86.Build.0 = Debug|Any CPU
{CBA8C535-CCA3-4F60-8D3E-0E25791CBD21}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CBA8C535-CCA3-4F60-8D3E-0E25791CBD21}.Release|Any CPU.Build.0 = Release|Any CPU
{CBA8C535-CCA3-4F60-8D3E-0E25791CBD21}.Release|ARM.ActiveCfg = Release|Any CPU
{CBA8C535-CCA3-4F60-8D3E-0E25791CBD21}.Release|ARM.Build.0 = Release|Any CPU
{CBA8C535-CCA3-4F60-8D3E-0E25791CBD21}.Release|x64.ActiveCfg = Release|Any CPU
{CBA8C535-CCA3-4F60-8D3E-0E25791CBD21}.Release|x64.Build.0 = Release|Any CPU
{CBA8C535-CCA3-4F60-8D3E-0E25791CBD21}.Release|x86.ActiveCfg = Release|Any CPU
{CBA8C535-CCA3-4F60-8D3E-0E25791CBD21}.Release|x86.Build.0 = Release|Any CPU
{177C4EF8-0B0A-426E-BDCC-168DC10AC1C1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{177C4EF8-0B0A-426E-BDCC-168DC10AC1C1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{177C4EF8-0B0A-426E-BDCC-168DC10AC1C1}.Debug|ARM.ActiveCfg = Debug|Any CPU
{177C4EF8-0B0A-426E-BDCC-168DC10AC1C1}.Debug|ARM.Build.0 = Debug|Any CPU
{177C4EF8-0B0A-426E-BDCC-168DC10AC1C1}.Debug|x64.ActiveCfg = Debug|Any CPU
{177C4EF8-0B0A-426E-BDCC-168DC10AC1C1}.Debug|x64.Build.0 = Debug|Any CPU
{177C4EF8-0B0A-426E-BDCC-168DC10AC1C1}.Debug|x86.ActiveCfg = Debug|Any CPU
{177C4EF8-0B0A-426E-BDCC-168DC10AC1C1}.Debug|x86.Build.0 = Debug|Any CPU
{177C4EF8-0B0A-426E-BDCC-168DC10AC1C1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{177C4EF8-0B0A-426E-BDCC-168DC10AC1C1}.Release|Any CPU.Build.0 = Release|Any CPU
{177C4EF8-0B0A-426E-BDCC-168DC10AC1C1}.Release|ARM.ActiveCfg = Release|Any CPU
{177C4EF8-0B0A-426E-BDCC-168DC10AC1C1}.Release|ARM.Build.0 = Release|Any CPU
{177C4EF8-0B0A-426E-BDCC-168DC10AC1C1}.Release|x64.ActiveCfg = Release|Any CPU
{177C4EF8-0B0A-426E-BDCC-168DC10AC1C1}.Release|x64.Build.0 = Release|Any CPU
{177C4EF8-0B0A-426E-BDCC-168DC10AC1C1}.Release|x86.ActiveCfg = Release|Any CPU
{177C4EF8-0B0A-426E-BDCC-168DC10AC1C1}.Release|x86.Build.0 = Release|Any CPU
{9949326E-9261-4F95-89B1-151F60498951}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9949326E-9261-4F95-89B1-151F60498951}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9949326E-9261-4F95-89B1-151F60498951}.Debug|ARM.ActiveCfg = Debug|Any CPU
{9949326E-9261-4F95-89B1-151F60498951}.Debug|ARM.Build.0 = Debug|Any CPU
{9949326E-9261-4F95-89B1-151F60498951}.Debug|x64.ActiveCfg = Debug|Any CPU
{9949326E-9261-4F95-89B1-151F60498951}.Debug|x64.Build.0 = Debug|Any CPU
{9949326E-9261-4F95-89B1-151F60498951}.Debug|x86.ActiveCfg = Debug|Any CPU
{9949326E-9261-4F95-89B1-151F60498951}.Debug|x86.Build.0 = Debug|Any CPU
{9949326E-9261-4F95-89B1-151F60498951}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9949326E-9261-4F95-89B1-151F60498951}.Release|Any CPU.Build.0 = Release|Any CPU
{9949326E-9261-4F95-89B1-151F60498951}.Release|ARM.ActiveCfg = Release|Any CPU
{9949326E-9261-4F95-89B1-151F60498951}.Release|ARM.Build.0 = Release|Any CPU
{9949326E-9261-4F95-89B1-151F60498951}.Release|x64.ActiveCfg = Release|Any CPU
{9949326E-9261-4F95-89B1-151F60498951}.Release|x64.Build.0 = Release|Any CPU
{9949326E-9261-4F95-89B1-151F60498951}.Release|x86.ActiveCfg = Release|Any CPU
{9949326E-9261-4F95-89B1-151F60498951}.Release|x86.Build.0 = Release|Any CPU
{AA62B4AA-1CA3-4C20-BEB7-B824D0FC4BD1}.Debug|Any CPU.ActiveCfg = Debug|ARM
{AA62B4AA-1CA3-4C20-BEB7-B824D0FC4BD1}.Debug|ARM.ActiveCfg = Debug|ARM
{AA62B4AA-1CA3-4C20-BEB7-B824D0FC4BD1}.Debug|ARM.Build.0 = Debug|ARM
{AA62B4AA-1CA3-4C20-BEB7-B824D0FC4BD1}.Debug|ARM.Deploy.0 = Debug|ARM
{AA62B4AA-1CA3-4C20-BEB7-B824D0FC4BD1}.Debug|x64.ActiveCfg = Debug|x64
{AA62B4AA-1CA3-4C20-BEB7-B824D0FC4BD1}.Debug|x64.Build.0 = Debug|x64
{AA62B4AA-1CA3-4C20-BEB7-B824D0FC4BD1}.Debug|x64.Deploy.0 = Debug|x64
{AA62B4AA-1CA3-4C20-BEB7-B824D0FC4BD1}.Debug|x86.ActiveCfg = Debug|x86
{AA62B4AA-1CA3-4C20-BEB7-B824D0FC4BD1}.Debug|x86.Build.0 = Debug|x86
{AA62B4AA-1CA3-4C20-BEB7-B824D0FC4BD1}.Debug|x86.Deploy.0 = Debug|x86
{AA62B4AA-1CA3-4C20-BEB7-B824D0FC4BD1}.Release|Any CPU.ActiveCfg = Release|x86
{AA62B4AA-1CA3-4C20-BEB7-B824D0FC4BD1}.Release|ARM.ActiveCfg = Release|ARM
{AA62B4AA-1CA3-4C20-BEB7-B824D0FC4BD1}.Release|ARM.Build.0 = Release|ARM
{AA62B4AA-1CA3-4C20-BEB7-B824D0FC4BD1}.Release|ARM.Deploy.0 = Release|ARM
{AA62B4AA-1CA3-4C20-BEB7-B824D0FC4BD1}.Release|x64.ActiveCfg = Release|x64
{AA62B4AA-1CA3-4C20-BEB7-B824D0FC4BD1}.Release|x64.Build.0 = Release|x64
{AA62B4AA-1CA3-4C20-BEB7-B824D0FC4BD1}.Release|x64.Deploy.0 = Release|x64
{AA62B4AA-1CA3-4C20-BEB7-B824D0FC4BD1}.Release|x86.ActiveCfg = Release|x86
{AA62B4AA-1CA3-4C20-BEB7-B824D0FC4BD1}.Release|x86.Build.0 = Release|x86
{AA62B4AA-1CA3-4C20-BEB7-B824D0FC4BD1}.Release|x86.Deploy.0 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{C7BF2B18-CC74-430B-BCB2-600304EFA3D8} = {AE8A7E02-0F7D-41B0-AB23-15394150ED17}
{EF44F661-B98A-4676-927F-85D138F82300} = {AE8A7E02-0F7D-41B0-AB23-15394150ED17}
{F789647E-96F7-43E3-A895-FA3FE8D01260} = {AE8A7E02-0F7D-41B0-AB23-15394150ED17}
{86470440-FEE2-4120-AF5A-3762FB9C536F} = {AE8A7E02-0F7D-41B0-AB23-15394150ED17}
{CBA8C535-CCA3-4F60-8D3E-0E25791CBD21} = {100879CC-8910-459E-856E-253D629E45DE}
{177C4EF8-0B0A-426E-BDCC-168DC10AC1C1} = {100879CC-8910-459E-856E-253D629E45DE}
{9949326E-9261-4F95-89B1-151F60498951} = {100879CC-8910-459E-856E-253D629E45DE}
{AA62B4AA-1CA3-4C20-BEB7-B824D0FC4BD1} = {100879CC-8910-459E-856E-253D629E45DE}
EndGlobalSection
EndGlobal

View file

@ -1,7 +1,7 @@
 
Microsoft Visual Studio Solution File, Format Version 12.00 Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 2013 # Visual Studio 14
VisualStudioVersion = 12.0.31101.0 VisualStudioVersion = 14.0.25420.1
MinimumVisualStudioVersion = 10.0.40219.1 MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FileDbCache.WinRT", "Caching\FileDbCache.WinRT\FileDbCache.WinRT.csproj", "{C7BF2B18-CC74-430B-BCB2-600304EFA3D8}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FileDbCache.WinRT", "Caching\FileDbCache.WinRT\FileDbCache.WinRT.csproj", "{C7BF2B18-CC74-430B-BCB2-600304EFA3D8}"
EndProject EndProject
@ -17,78 +17,210 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MapControl.WinRT", "MapCont
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MapControl.WPF", "MapControl\MapControl.WPF.csproj", "{226F3575-B683-446D-A2F0-181291DC8787}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MapControl.WPF", "MapControl\MapControl.WPF.csproj", "{226F3575-B683-446D-A2F0-181291DC8787}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PhoneApplication", "SampleApps\PhoneApplication\PhoneApplication.csproj", "{8D0A57DF-FABF-4AEE-8768-9C18B2B43CA9}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SilverlightApplication", "SampleApps\SilverlightApplication\SilverlightApplication.csproj", "{CBA8C535-CCA3-4F60-8D3E-0E25791CBD21}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SilverlightApplication.Web", "SampleApps\SilverlightApplication.Web\SilverlightApplication.Web.csproj", "{177C4EF8-0B0A-426E-BDCC-168DC10AC1C1}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StoreApplication", "SampleApps\StoreApplication\StoreApplication.csproj", "{747A3F84-E11F-4EC8-9463-98BBB1E0D0A4}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WpfApplication", "SampleApps\WpfApplication\WpfApplication.csproj", "{9949326E-9261-4F95-89B1-151F60498951}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WpfApplication", "SampleApps\WpfApplication\WpfApplication.csproj", "{9949326E-9261-4F95-89B1-151F60498951}"
EndProject EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SampleApps", "SampleApps", "{100879CC-8910-459E-856E-253D629E45DE}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SampleApps", "SampleApps", "{100879CC-8910-459E-856E-253D629E45DE}"
EndProject EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Caching", "Caching", "{AE8A7E02-0F7D-41B0-AB23-15394150ED17}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Caching", "Caching", "{AE8A7E02-0F7D-41B0-AB23-15394150ED17}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UniversalApp", "SampleApps\UniversalApp\UniversalApp.csproj", "{AA62B4AA-1CA3-4C20-BEB7-B824D0FC4BD1}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SilverlightApplication", "SampleApps\SilverlightApplication\SilverlightApplication.csproj", "{85AACDB7-959D-406D-A8DF-2F1E013F8F40}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SilverlightApplication.Web", "SampleApps\SilverlightApplication.Web\SilverlightApplication.Web.csproj", "{7FB616E1-E0D4-48FA-8AFD-60E73FCF32E4}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
Debug|ARM = Debug|ARM
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU Release|Any CPU = Release|Any CPU
Release|ARM = Release|ARM
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution GlobalSection(ProjectConfigurationPlatforms) = postSolution
{C7BF2B18-CC74-430B-BCB2-600304EFA3D8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C7BF2B18-CC74-430B-BCB2-600304EFA3D8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C7BF2B18-CC74-430B-BCB2-600304EFA3D8}.Debug|Any CPU.Build.0 = Debug|Any CPU {C7BF2B18-CC74-430B-BCB2-600304EFA3D8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C7BF2B18-CC74-430B-BCB2-600304EFA3D8}.Debug|ARM.ActiveCfg = Debug|Any CPU
{C7BF2B18-CC74-430B-BCB2-600304EFA3D8}.Debug|ARM.Build.0 = Debug|Any CPU
{C7BF2B18-CC74-430B-BCB2-600304EFA3D8}.Debug|x64.ActiveCfg = Debug|Any CPU
{C7BF2B18-CC74-430B-BCB2-600304EFA3D8}.Debug|x64.Build.0 = Debug|Any CPU
{C7BF2B18-CC74-430B-BCB2-600304EFA3D8}.Debug|x86.ActiveCfg = Debug|Any CPU
{C7BF2B18-CC74-430B-BCB2-600304EFA3D8}.Debug|x86.Build.0 = Debug|Any CPU
{C7BF2B18-CC74-430B-BCB2-600304EFA3D8}.Release|Any CPU.ActiveCfg = Release|Any CPU {C7BF2B18-CC74-430B-BCB2-600304EFA3D8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C7BF2B18-CC74-430B-BCB2-600304EFA3D8}.Release|Any CPU.Build.0 = Release|Any CPU {C7BF2B18-CC74-430B-BCB2-600304EFA3D8}.Release|Any CPU.Build.0 = Release|Any CPU
{C7BF2B18-CC74-430B-BCB2-600304EFA3D8}.Release|ARM.ActiveCfg = Release|Any CPU
{C7BF2B18-CC74-430B-BCB2-600304EFA3D8}.Release|ARM.Build.0 = Release|Any CPU
{C7BF2B18-CC74-430B-BCB2-600304EFA3D8}.Release|x64.ActiveCfg = Release|Any CPU
{C7BF2B18-CC74-430B-BCB2-600304EFA3D8}.Release|x64.Build.0 = Release|Any CPU
{C7BF2B18-CC74-430B-BCB2-600304EFA3D8}.Release|x86.ActiveCfg = Release|Any CPU
{C7BF2B18-CC74-430B-BCB2-600304EFA3D8}.Release|x86.Build.0 = Release|Any CPU
{EF44F661-B98A-4676-927F-85D138F82300}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {EF44F661-B98A-4676-927F-85D138F82300}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EF44F661-B98A-4676-927F-85D138F82300}.Debug|Any CPU.Build.0 = Debug|Any CPU {EF44F661-B98A-4676-927F-85D138F82300}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EF44F661-B98A-4676-927F-85D138F82300}.Debug|ARM.ActiveCfg = Debug|Any CPU
{EF44F661-B98A-4676-927F-85D138F82300}.Debug|ARM.Build.0 = Debug|Any CPU
{EF44F661-B98A-4676-927F-85D138F82300}.Debug|x64.ActiveCfg = Debug|Any CPU
{EF44F661-B98A-4676-927F-85D138F82300}.Debug|x64.Build.0 = Debug|Any CPU
{EF44F661-B98A-4676-927F-85D138F82300}.Debug|x86.ActiveCfg = Debug|Any CPU
{EF44F661-B98A-4676-927F-85D138F82300}.Debug|x86.Build.0 = Debug|Any CPU
{EF44F661-B98A-4676-927F-85D138F82300}.Release|Any CPU.ActiveCfg = Release|Any CPU {EF44F661-B98A-4676-927F-85D138F82300}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EF44F661-B98A-4676-927F-85D138F82300}.Release|Any CPU.Build.0 = Release|Any CPU {EF44F661-B98A-4676-927F-85D138F82300}.Release|Any CPU.Build.0 = Release|Any CPU
{EF44F661-B98A-4676-927F-85D138F82300}.Release|ARM.ActiveCfg = Release|Any CPU
{EF44F661-B98A-4676-927F-85D138F82300}.Release|ARM.Build.0 = Release|Any CPU
{EF44F661-B98A-4676-927F-85D138F82300}.Release|x64.ActiveCfg = Release|Any CPU
{EF44F661-B98A-4676-927F-85D138F82300}.Release|x64.Build.0 = Release|Any CPU
{EF44F661-B98A-4676-927F-85D138F82300}.Release|x86.ActiveCfg = Release|Any CPU
{EF44F661-B98A-4676-927F-85D138F82300}.Release|x86.Build.0 = Release|Any CPU
{F789647E-96F7-43E3-A895-FA3FE8D01260}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F789647E-96F7-43E3-A895-FA3FE8D01260}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F789647E-96F7-43E3-A895-FA3FE8D01260}.Debug|Any CPU.Build.0 = Debug|Any CPU {F789647E-96F7-43E3-A895-FA3FE8D01260}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F789647E-96F7-43E3-A895-FA3FE8D01260}.Debug|ARM.ActiveCfg = Debug|Any CPU
{F789647E-96F7-43E3-A895-FA3FE8D01260}.Debug|ARM.Build.0 = Debug|Any CPU
{F789647E-96F7-43E3-A895-FA3FE8D01260}.Debug|x64.ActiveCfg = Debug|Any CPU
{F789647E-96F7-43E3-A895-FA3FE8D01260}.Debug|x64.Build.0 = Debug|Any CPU
{F789647E-96F7-43E3-A895-FA3FE8D01260}.Debug|x86.ActiveCfg = Debug|Any CPU
{F789647E-96F7-43E3-A895-FA3FE8D01260}.Debug|x86.Build.0 = Debug|Any CPU
{F789647E-96F7-43E3-A895-FA3FE8D01260}.Release|Any CPU.ActiveCfg = Release|Any CPU {F789647E-96F7-43E3-A895-FA3FE8D01260}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F789647E-96F7-43E3-A895-FA3FE8D01260}.Release|Any CPU.Build.0 = Release|Any CPU {F789647E-96F7-43E3-A895-FA3FE8D01260}.Release|Any CPU.Build.0 = Release|Any CPU
{F789647E-96F7-43E3-A895-FA3FE8D01260}.Release|ARM.ActiveCfg = Release|Any CPU
{F789647E-96F7-43E3-A895-FA3FE8D01260}.Release|ARM.Build.0 = Release|Any CPU
{F789647E-96F7-43E3-A895-FA3FE8D01260}.Release|x64.ActiveCfg = Release|Any CPU
{F789647E-96F7-43E3-A895-FA3FE8D01260}.Release|x64.Build.0 = Release|Any CPU
{F789647E-96F7-43E3-A895-FA3FE8D01260}.Release|x86.ActiveCfg = Release|Any CPU
{F789647E-96F7-43E3-A895-FA3FE8D01260}.Release|x86.Build.0 = Release|Any CPU
{86470440-FEE2-4120-AF5A-3762FB9C536F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {86470440-FEE2-4120-AF5A-3762FB9C536F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{86470440-FEE2-4120-AF5A-3762FB9C536F}.Debug|Any CPU.Build.0 = Debug|Any CPU {86470440-FEE2-4120-AF5A-3762FB9C536F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{86470440-FEE2-4120-AF5A-3762FB9C536F}.Debug|ARM.ActiveCfg = Debug|Any CPU
{86470440-FEE2-4120-AF5A-3762FB9C536F}.Debug|ARM.Build.0 = Debug|Any CPU
{86470440-FEE2-4120-AF5A-3762FB9C536F}.Debug|x64.ActiveCfg = Debug|Any CPU
{86470440-FEE2-4120-AF5A-3762FB9C536F}.Debug|x64.Build.0 = Debug|Any CPU
{86470440-FEE2-4120-AF5A-3762FB9C536F}.Debug|x86.ActiveCfg = Debug|Any CPU
{86470440-FEE2-4120-AF5A-3762FB9C536F}.Debug|x86.Build.0 = Debug|Any CPU
{86470440-FEE2-4120-AF5A-3762FB9C536F}.Release|Any CPU.ActiveCfg = Release|Any CPU {86470440-FEE2-4120-AF5A-3762FB9C536F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{86470440-FEE2-4120-AF5A-3762FB9C536F}.Release|Any CPU.Build.0 = Release|Any CPU {86470440-FEE2-4120-AF5A-3762FB9C536F}.Release|Any CPU.Build.0 = Release|Any CPU
{86470440-FEE2-4120-AF5A-3762FB9C536F}.Release|ARM.ActiveCfg = Release|Any CPU
{86470440-FEE2-4120-AF5A-3762FB9C536F}.Release|ARM.Build.0 = Release|Any CPU
{86470440-FEE2-4120-AF5A-3762FB9C536F}.Release|x64.ActiveCfg = Release|Any CPU
{86470440-FEE2-4120-AF5A-3762FB9C536F}.Release|x64.Build.0 = Release|Any CPU
{86470440-FEE2-4120-AF5A-3762FB9C536F}.Release|x86.ActiveCfg = Release|Any CPU
{86470440-FEE2-4120-AF5A-3762FB9C536F}.Release|x86.Build.0 = Release|Any CPU
{EB133B78-DEFF-416A-8F0C-89E54D766576}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {EB133B78-DEFF-416A-8F0C-89E54D766576}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EB133B78-DEFF-416A-8F0C-89E54D766576}.Debug|Any CPU.Build.0 = Debug|Any CPU {EB133B78-DEFF-416A-8F0C-89E54D766576}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EB133B78-DEFF-416A-8F0C-89E54D766576}.Debug|ARM.ActiveCfg = Debug|Any CPU
{EB133B78-DEFF-416A-8F0C-89E54D766576}.Debug|ARM.Build.0 = Debug|Any CPU
{EB133B78-DEFF-416A-8F0C-89E54D766576}.Debug|x64.ActiveCfg = Debug|Any CPU
{EB133B78-DEFF-416A-8F0C-89E54D766576}.Debug|x64.Build.0 = Debug|Any CPU
{EB133B78-DEFF-416A-8F0C-89E54D766576}.Debug|x86.ActiveCfg = Debug|Any CPU
{EB133B78-DEFF-416A-8F0C-89E54D766576}.Debug|x86.Build.0 = Debug|Any CPU
{EB133B78-DEFF-416A-8F0C-89E54D766576}.Release|Any CPU.ActiveCfg = Release|Any CPU {EB133B78-DEFF-416A-8F0C-89E54D766576}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EB133B78-DEFF-416A-8F0C-89E54D766576}.Release|Any CPU.Build.0 = Release|Any CPU {EB133B78-DEFF-416A-8F0C-89E54D766576}.Release|Any CPU.Build.0 = Release|Any CPU
{EB133B78-DEFF-416A-8F0C-89E54D766576}.Release|ARM.ActiveCfg = Release|Any CPU
{EB133B78-DEFF-416A-8F0C-89E54D766576}.Release|ARM.Build.0 = Release|Any CPU
{EB133B78-DEFF-416A-8F0C-89E54D766576}.Release|x64.ActiveCfg = Release|Any CPU
{EB133B78-DEFF-416A-8F0C-89E54D766576}.Release|x64.Build.0 = Release|Any CPU
{EB133B78-DEFF-416A-8F0C-89E54D766576}.Release|x86.ActiveCfg = Release|Any CPU
{EB133B78-DEFF-416A-8F0C-89E54D766576}.Release|x86.Build.0 = Release|Any CPU
{63CEFDF7-5170-43B6-86F8-5C4A383A1615}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {63CEFDF7-5170-43B6-86F8-5C4A383A1615}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{63CEFDF7-5170-43B6-86F8-5C4A383A1615}.Debug|Any CPU.Build.0 = Debug|Any CPU {63CEFDF7-5170-43B6-86F8-5C4A383A1615}.Debug|Any CPU.Build.0 = Debug|Any CPU
{63CEFDF7-5170-43B6-86F8-5C4A383A1615}.Debug|ARM.ActiveCfg = Debug|Any CPU
{63CEFDF7-5170-43B6-86F8-5C4A383A1615}.Debug|ARM.Build.0 = Debug|Any CPU
{63CEFDF7-5170-43B6-86F8-5C4A383A1615}.Debug|x64.ActiveCfg = Debug|Any CPU
{63CEFDF7-5170-43B6-86F8-5C4A383A1615}.Debug|x64.Build.0 = Debug|Any CPU
{63CEFDF7-5170-43B6-86F8-5C4A383A1615}.Debug|x86.ActiveCfg = Debug|Any CPU
{63CEFDF7-5170-43B6-86F8-5C4A383A1615}.Debug|x86.Build.0 = Debug|Any CPU
{63CEFDF7-5170-43B6-86F8-5C4A383A1615}.Release|Any CPU.ActiveCfg = Release|Any CPU {63CEFDF7-5170-43B6-86F8-5C4A383A1615}.Release|Any CPU.ActiveCfg = Release|Any CPU
{63CEFDF7-5170-43B6-86F8-5C4A383A1615}.Release|Any CPU.Build.0 = Release|Any CPU {63CEFDF7-5170-43B6-86F8-5C4A383A1615}.Release|Any CPU.Build.0 = Release|Any CPU
{63CEFDF7-5170-43B6-86F8-5C4A383A1615}.Release|ARM.ActiveCfg = Release|Any CPU
{63CEFDF7-5170-43B6-86F8-5C4A383A1615}.Release|ARM.Build.0 = Release|Any CPU
{63CEFDF7-5170-43B6-86F8-5C4A383A1615}.Release|x64.ActiveCfg = Release|Any CPU
{63CEFDF7-5170-43B6-86F8-5C4A383A1615}.Release|x64.Build.0 = Release|Any CPU
{63CEFDF7-5170-43B6-86F8-5C4A383A1615}.Release|x86.ActiveCfg = Release|Any CPU
{63CEFDF7-5170-43B6-86F8-5C4A383A1615}.Release|x86.Build.0 = Release|Any CPU
{226F3575-B683-446D-A2F0-181291DC8787}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {226F3575-B683-446D-A2F0-181291DC8787}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{226F3575-B683-446D-A2F0-181291DC8787}.Debug|Any CPU.Build.0 = Debug|Any CPU {226F3575-B683-446D-A2F0-181291DC8787}.Debug|Any CPU.Build.0 = Debug|Any CPU
{226F3575-B683-446D-A2F0-181291DC8787}.Debug|ARM.ActiveCfg = Debug|Any CPU
{226F3575-B683-446D-A2F0-181291DC8787}.Debug|ARM.Build.0 = Debug|Any CPU
{226F3575-B683-446D-A2F0-181291DC8787}.Debug|x64.ActiveCfg = Debug|Any CPU
{226F3575-B683-446D-A2F0-181291DC8787}.Debug|x64.Build.0 = Debug|Any CPU
{226F3575-B683-446D-A2F0-181291DC8787}.Debug|x86.ActiveCfg = Debug|Any CPU
{226F3575-B683-446D-A2F0-181291DC8787}.Debug|x86.Build.0 = Debug|Any CPU
{226F3575-B683-446D-A2F0-181291DC8787}.Release|Any CPU.ActiveCfg = Release|Any CPU {226F3575-B683-446D-A2F0-181291DC8787}.Release|Any CPU.ActiveCfg = Release|Any CPU
{226F3575-B683-446D-A2F0-181291DC8787}.Release|Any CPU.Build.0 = Release|Any CPU {226F3575-B683-446D-A2F0-181291DC8787}.Release|Any CPU.Build.0 = Release|Any CPU
{8D0A57DF-FABF-4AEE-8768-9C18B2B43CA9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {226F3575-B683-446D-A2F0-181291DC8787}.Release|ARM.ActiveCfg = Release|Any CPU
{8D0A57DF-FABF-4AEE-8768-9C18B2B43CA9}.Debug|Any CPU.Build.0 = Debug|Any CPU {226F3575-B683-446D-A2F0-181291DC8787}.Release|ARM.Build.0 = Release|Any CPU
{8D0A57DF-FABF-4AEE-8768-9C18B2B43CA9}.Debug|Any CPU.Deploy.0 = Debug|Any CPU {226F3575-B683-446D-A2F0-181291DC8787}.Release|x64.ActiveCfg = Release|Any CPU
{8D0A57DF-FABF-4AEE-8768-9C18B2B43CA9}.Release|Any CPU.ActiveCfg = Release|Any CPU {226F3575-B683-446D-A2F0-181291DC8787}.Release|x64.Build.0 = Release|Any CPU
{8D0A57DF-FABF-4AEE-8768-9C18B2B43CA9}.Release|Any CPU.Build.0 = Release|Any CPU {226F3575-B683-446D-A2F0-181291DC8787}.Release|x86.ActiveCfg = Release|Any CPU
{8D0A57DF-FABF-4AEE-8768-9C18B2B43CA9}.Release|Any CPU.Deploy.0 = Release|Any CPU {226F3575-B683-446D-A2F0-181291DC8787}.Release|x86.Build.0 = Release|Any CPU
{CBA8C535-CCA3-4F60-8D3E-0E25791CBD21}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CBA8C535-CCA3-4F60-8D3E-0E25791CBD21}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CBA8C535-CCA3-4F60-8D3E-0E25791CBD21}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CBA8C535-CCA3-4F60-8D3E-0E25791CBD21}.Release|Any CPU.Build.0 = Release|Any CPU
{177C4EF8-0B0A-426E-BDCC-168DC10AC1C1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{177C4EF8-0B0A-426E-BDCC-168DC10AC1C1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{177C4EF8-0B0A-426E-BDCC-168DC10AC1C1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{177C4EF8-0B0A-426E-BDCC-168DC10AC1C1}.Release|Any CPU.Build.0 = Release|Any CPU
{747A3F84-E11F-4EC8-9463-98BBB1E0D0A4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{747A3F84-E11F-4EC8-9463-98BBB1E0D0A4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{747A3F84-E11F-4EC8-9463-98BBB1E0D0A4}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
{747A3F84-E11F-4EC8-9463-98BBB1E0D0A4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{747A3F84-E11F-4EC8-9463-98BBB1E0D0A4}.Release|Any CPU.Build.0 = Release|Any CPU
{747A3F84-E11F-4EC8-9463-98BBB1E0D0A4}.Release|Any CPU.Deploy.0 = Release|Any CPU
{9949326E-9261-4F95-89B1-151F60498951}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {9949326E-9261-4F95-89B1-151F60498951}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9949326E-9261-4F95-89B1-151F60498951}.Debug|Any CPU.Build.0 = Debug|Any CPU {9949326E-9261-4F95-89B1-151F60498951}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9949326E-9261-4F95-89B1-151F60498951}.Debug|ARM.ActiveCfg = Debug|Any CPU
{9949326E-9261-4F95-89B1-151F60498951}.Debug|ARM.Build.0 = Debug|Any CPU
{9949326E-9261-4F95-89B1-151F60498951}.Debug|x64.ActiveCfg = Debug|Any CPU
{9949326E-9261-4F95-89B1-151F60498951}.Debug|x64.Build.0 = Debug|Any CPU
{9949326E-9261-4F95-89B1-151F60498951}.Debug|x86.ActiveCfg = Debug|Any CPU
{9949326E-9261-4F95-89B1-151F60498951}.Debug|x86.Build.0 = Debug|Any CPU
{9949326E-9261-4F95-89B1-151F60498951}.Release|Any CPU.ActiveCfg = Release|Any CPU {9949326E-9261-4F95-89B1-151F60498951}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9949326E-9261-4F95-89B1-151F60498951}.Release|Any CPU.Build.0 = Release|Any CPU {9949326E-9261-4F95-89B1-151F60498951}.Release|Any CPU.Build.0 = Release|Any CPU
{9949326E-9261-4F95-89B1-151F60498951}.Release|ARM.ActiveCfg = Release|Any CPU
{9949326E-9261-4F95-89B1-151F60498951}.Release|ARM.Build.0 = Release|Any CPU
{9949326E-9261-4F95-89B1-151F60498951}.Release|x64.ActiveCfg = Release|Any CPU
{9949326E-9261-4F95-89B1-151F60498951}.Release|x64.Build.0 = Release|Any CPU
{9949326E-9261-4F95-89B1-151F60498951}.Release|x86.ActiveCfg = Release|Any CPU
{9949326E-9261-4F95-89B1-151F60498951}.Release|x86.Build.0 = Release|Any CPU
{AA62B4AA-1CA3-4C20-BEB7-B824D0FC4BD1}.Debug|Any CPU.ActiveCfg = Debug|ARM
{AA62B4AA-1CA3-4C20-BEB7-B824D0FC4BD1}.Debug|ARM.ActiveCfg = Debug|ARM
{AA62B4AA-1CA3-4C20-BEB7-B824D0FC4BD1}.Debug|ARM.Build.0 = Debug|ARM
{AA62B4AA-1CA3-4C20-BEB7-B824D0FC4BD1}.Debug|ARM.Deploy.0 = Debug|ARM
{AA62B4AA-1CA3-4C20-BEB7-B824D0FC4BD1}.Debug|x64.ActiveCfg = Debug|x64
{AA62B4AA-1CA3-4C20-BEB7-B824D0FC4BD1}.Debug|x64.Build.0 = Debug|x64
{AA62B4AA-1CA3-4C20-BEB7-B824D0FC4BD1}.Debug|x64.Deploy.0 = Debug|x64
{AA62B4AA-1CA3-4C20-BEB7-B824D0FC4BD1}.Debug|x86.ActiveCfg = Debug|x86
{AA62B4AA-1CA3-4C20-BEB7-B824D0FC4BD1}.Debug|x86.Build.0 = Debug|x86
{AA62B4AA-1CA3-4C20-BEB7-B824D0FC4BD1}.Debug|x86.Deploy.0 = Debug|x86
{AA62B4AA-1CA3-4C20-BEB7-B824D0FC4BD1}.Release|Any CPU.ActiveCfg = Release|x86
{AA62B4AA-1CA3-4C20-BEB7-B824D0FC4BD1}.Release|ARM.ActiveCfg = Release|ARM
{AA62B4AA-1CA3-4C20-BEB7-B824D0FC4BD1}.Release|ARM.Build.0 = Release|ARM
{AA62B4AA-1CA3-4C20-BEB7-B824D0FC4BD1}.Release|ARM.Deploy.0 = Release|ARM
{AA62B4AA-1CA3-4C20-BEB7-B824D0FC4BD1}.Release|x64.ActiveCfg = Release|x64
{AA62B4AA-1CA3-4C20-BEB7-B824D0FC4BD1}.Release|x64.Build.0 = Release|x64
{AA62B4AA-1CA3-4C20-BEB7-B824D0FC4BD1}.Release|x64.Deploy.0 = Release|x64
{AA62B4AA-1CA3-4C20-BEB7-B824D0FC4BD1}.Release|x86.ActiveCfg = Release|x86
{AA62B4AA-1CA3-4C20-BEB7-B824D0FC4BD1}.Release|x86.Build.0 = Release|x86
{AA62B4AA-1CA3-4C20-BEB7-B824D0FC4BD1}.Release|x86.Deploy.0 = Release|x86
{85AACDB7-959D-406D-A8DF-2F1E013F8F40}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{85AACDB7-959D-406D-A8DF-2F1E013F8F40}.Debug|Any CPU.Build.0 = Debug|Any CPU
{85AACDB7-959D-406D-A8DF-2F1E013F8F40}.Debug|ARM.ActiveCfg = Debug|Any CPU
{85AACDB7-959D-406D-A8DF-2F1E013F8F40}.Debug|ARM.Build.0 = Debug|Any CPU
{85AACDB7-959D-406D-A8DF-2F1E013F8F40}.Debug|x64.ActiveCfg = Debug|Any CPU
{85AACDB7-959D-406D-A8DF-2F1E013F8F40}.Debug|x64.Build.0 = Debug|Any CPU
{85AACDB7-959D-406D-A8DF-2F1E013F8F40}.Debug|x86.ActiveCfg = Debug|Any CPU
{85AACDB7-959D-406D-A8DF-2F1E013F8F40}.Debug|x86.Build.0 = Debug|Any CPU
{85AACDB7-959D-406D-A8DF-2F1E013F8F40}.Release|Any CPU.ActiveCfg = Release|Any CPU
{85AACDB7-959D-406D-A8DF-2F1E013F8F40}.Release|Any CPU.Build.0 = Release|Any CPU
{85AACDB7-959D-406D-A8DF-2F1E013F8F40}.Release|ARM.ActiveCfg = Release|Any CPU
{85AACDB7-959D-406D-A8DF-2F1E013F8F40}.Release|ARM.Build.0 = Release|Any CPU
{85AACDB7-959D-406D-A8DF-2F1E013F8F40}.Release|x64.ActiveCfg = Release|Any CPU
{85AACDB7-959D-406D-A8DF-2F1E013F8F40}.Release|x64.Build.0 = Release|Any CPU
{85AACDB7-959D-406D-A8DF-2F1E013F8F40}.Release|x86.ActiveCfg = Release|Any CPU
{85AACDB7-959D-406D-A8DF-2F1E013F8F40}.Release|x86.Build.0 = Release|Any CPU
{7FB616E1-E0D4-48FA-8AFD-60E73FCF32E4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7FB616E1-E0D4-48FA-8AFD-60E73FCF32E4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7FB616E1-E0D4-48FA-8AFD-60E73FCF32E4}.Debug|ARM.ActiveCfg = Debug|Any CPU
{7FB616E1-E0D4-48FA-8AFD-60E73FCF32E4}.Debug|ARM.Build.0 = Debug|Any CPU
{7FB616E1-E0D4-48FA-8AFD-60E73FCF32E4}.Debug|x64.ActiveCfg = Debug|Any CPU
{7FB616E1-E0D4-48FA-8AFD-60E73FCF32E4}.Debug|x64.Build.0 = Debug|Any CPU
{7FB616E1-E0D4-48FA-8AFD-60E73FCF32E4}.Debug|x86.ActiveCfg = Debug|Any CPU
{7FB616E1-E0D4-48FA-8AFD-60E73FCF32E4}.Debug|x86.Build.0 = Debug|Any CPU
{7FB616E1-E0D4-48FA-8AFD-60E73FCF32E4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7FB616E1-E0D4-48FA-8AFD-60E73FCF32E4}.Release|Any CPU.Build.0 = Release|Any CPU
{7FB616E1-E0D4-48FA-8AFD-60E73FCF32E4}.Release|ARM.ActiveCfg = Release|Any CPU
{7FB616E1-E0D4-48FA-8AFD-60E73FCF32E4}.Release|ARM.Build.0 = Release|Any CPU
{7FB616E1-E0D4-48FA-8AFD-60E73FCF32E4}.Release|x64.ActiveCfg = Release|Any CPU
{7FB616E1-E0D4-48FA-8AFD-60E73FCF32E4}.Release|x64.Build.0 = Release|Any CPU
{7FB616E1-E0D4-48FA-8AFD-60E73FCF32E4}.Release|x86.ActiveCfg = Release|Any CPU
{7FB616E1-E0D4-48FA-8AFD-60E73FCF32E4}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
@ -98,10 +230,9 @@ Global
{EF44F661-B98A-4676-927F-85D138F82300} = {AE8A7E02-0F7D-41B0-AB23-15394150ED17} {EF44F661-B98A-4676-927F-85D138F82300} = {AE8A7E02-0F7D-41B0-AB23-15394150ED17}
{F789647E-96F7-43E3-A895-FA3FE8D01260} = {AE8A7E02-0F7D-41B0-AB23-15394150ED17} {F789647E-96F7-43E3-A895-FA3FE8D01260} = {AE8A7E02-0F7D-41B0-AB23-15394150ED17}
{86470440-FEE2-4120-AF5A-3762FB9C536F} = {AE8A7E02-0F7D-41B0-AB23-15394150ED17} {86470440-FEE2-4120-AF5A-3762FB9C536F} = {AE8A7E02-0F7D-41B0-AB23-15394150ED17}
{8D0A57DF-FABF-4AEE-8768-9C18B2B43CA9} = {100879CC-8910-459E-856E-253D629E45DE}
{CBA8C535-CCA3-4F60-8D3E-0E25791CBD21} = {100879CC-8910-459E-856E-253D629E45DE}
{177C4EF8-0B0A-426E-BDCC-168DC10AC1C1} = {100879CC-8910-459E-856E-253D629E45DE}
{747A3F84-E11F-4EC8-9463-98BBB1E0D0A4} = {100879CC-8910-459E-856E-253D629E45DE}
{9949326E-9261-4F95-89B1-151F60498951} = {100879CC-8910-459E-856E-253D629E45DE} {9949326E-9261-4F95-89B1-151F60498951} = {100879CC-8910-459E-856E-253D629E45DE}
{AA62B4AA-1CA3-4C20-BEB7-B824D0FC4BD1} = {100879CC-8910-459E-856E-253D629E45DE}
{85AACDB7-959D-406D-A8DF-2F1E013F8F40} = {100879CC-8910-459E-856E-253D629E45DE}
{7FB616E1-E0D4-48FA-8AFD-60E73FCF32E4} = {100879CC-8910-459E-856E-253D629E45DE}
EndGlobalSection EndGlobalSection
EndGlobal EndGlobal

View file

@ -0,0 +1,58 @@
// 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;
#else
using System.Windows;
#endif
namespace MapControl
{
/// <summary>
/// Transforms map coordinates according to the Azimuthal Equidistant Projection.
/// </summary>
public class AzimuthalEquidistantProjection : AzimuthalProjection
{
public AzimuthalEquidistantProjection()
{
// No known standard or de-facto standard CRS ID
}
public AzimuthalEquidistantProjection(string crsId)
{
CrsId = crsId;
}
public override Point LocationToPoint(Location location)
{
if (location.Equals(projectionCenter))
{
return new Point();
}
double azimuth, distance;
GetAzimuthDistance(projectionCenter, location, out azimuth, out distance);
distance *= Wgs84EquatorialRadius;
return new Point(distance * Math.Sin(azimuth), distance * Math.Cos(azimuth));
}
public override Location PointToLocation(Point point)
{
if (point.X == 0d && point.Y == 0d)
{
return projectionCenter;
}
var azimuth = Math.Atan2(point.X, point.Y);
var distance = Math.Sqrt(point.X * point.X + point.Y * point.Y) / Wgs84EquatorialRadius;
return GetLocation(projectionCenter, azimuth, distance);
}
}
}

View file

@ -0,0 +1,137 @@
// 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.Globalization;
#if NETFX_CORE
using Windows.Foundation;
#else
using System.Windows;
#endif
namespace MapControl
{
/// <summary>
/// Base class for azimuthal map projections.
/// </summary>
public abstract class AzimuthalProjection : MapProjection
{
protected Location projectionCenter = new Location();
public AzimuthalProjection()
{
IsAzimuthal = true;
LongitudeScale = double.NaN;
}
public override double GetViewportScale(double zoomLevel)
{
return base.GetViewportScale(zoomLevel) / MetersPerDegree;
}
public override Point GetMapScale(Location location)
{
return new Point(ViewportScale, ViewportScale);
}
public override Location TranslateLocation(Location location, Point translation)
{
var scaleY = ViewportScale * MetersPerDegree;
var scaleX = scaleY * Math.Cos(location.Latitude * Math.PI / 180d);
return new Location(
location.Latitude - translation.Y / scaleY,
location.Longitude + translation.X / scaleX);
}
public override Rect BoundingBoxToRect(BoundingBox boundingBox)
{
var cbbox = boundingBox as CenteredBoundingBox;
if (cbbox != null)
{
var center = LocationToPoint(cbbox.Center);
return new Rect(
center.X - cbbox.Width / 2d, center.Y - cbbox.Height / 2d,
cbbox.Width, cbbox.Height);
}
return base.BoundingBoxToRect(boundingBox);
}
public override BoundingBox RectToBoundingBox(Rect rect)
{
var center = PointToLocation(new Point(rect.X + rect.Width / 2d, rect.Y + rect.Height / 2d));
return new CenteredBoundingBox(center, rect.Width, rect.Height); // width and height in meters
}
public override void SetViewportTransform(Location projectionCenter, Location mapCenter, Point viewportCenter, double zoomLevel, double heading)
{
this.projectionCenter = projectionCenter;
base.SetViewportTransform(projectionCenter, mapCenter, viewportCenter, zoomLevel, heading);
}
public override string WmsQueryParameters(BoundingBox boundingBox, string version)
{
if (string.IsNullOrEmpty(CrsId))
{
return null;
}
var rect = BoundingBoxToRect(boundingBox);
var width = (int)Math.Round(ViewportScale * rect.Width);
var height = (int)Math.Round(ViewportScale * rect.Height);
var crs = version.StartsWith("1.1.") ? "SRS" : "CRS";
return string.Format(CultureInfo.InvariantCulture,
"{0}={1},1,{2},{3}&BBOX={4},{5},{6},{7}&WIDTH={8}&HEIGHT={9}",
crs, CrsId, projectionCenter.Longitude, projectionCenter.Latitude,
rect.X, rect.Y, (rect.X + rect.Width), (rect.Y + rect.Height), width, height);
}
/// <summary>
/// Calculates azimuth and distance in radians from location1 to location2.
/// The returned distance has to be multiplied with an appropriate earth radius.
/// </summary>
public static void GetAzimuthDistance(Location location1, Location location2, out double azimuth, out double distance)
{
var lat1 = location1.Latitude * Math.PI / 180d;
var lon1 = location1.Longitude * Math.PI / 180d;
var lat2 = location2.Latitude * Math.PI / 180d;
var lon2 = location2.Longitude * Math.PI / 180d;
var cosLat1 = Math.Cos(lat1);
var sinLat1 = Math.Sin(lat1);
var cosLat2 = Math.Cos(lat2);
var sinLat2 = Math.Sin(lat2);
var cosLon12 = Math.Cos(lon2 - lon1);
var sinLon12 = Math.Sin(lon2 - lon1);
var cosDistance = sinLat1 * sinLat2 + cosLat1 * cosLat2 * cosLon12;
azimuth = Math.Atan2(sinLon12, cosLat1 * sinLat2 / cosLat2 - sinLat1 * cosLon12);
distance = Math.Acos(Math.Max(Math.Min(cosDistance, 1d), -1d));
}
/// <summary>
/// Calculates the Location of the point given by azimuth and distance in radians from location.
/// </summary>
public static Location GetLocation(Location location, double azimuth, double distance)
{
var lat1 = location.Latitude * Math.PI / 180d;
var sinDistance = Math.Sin(distance);
var cosDistance = Math.Cos(distance);
var cosAzimuth = Math.Cos(azimuth);
var sinAzimuth = Math.Sin(azimuth);
var cosLat1 = Math.Cos(lat1);
var sinLat1 = Math.Sin(lat1);
var sinLat2 = sinLat1 * cosDistance + cosLat1 * sinDistance * cosAzimuth;
var lat2 = Math.Asin(Math.Max(Math.Min(sinLat2, 1d), -1d));
var dLon = Math.Atan2(sinDistance * sinAzimuth, cosLat1 * cosDistance - sinLat1 * sinDistance * cosAzimuth);
return new Location(180d / Math.PI * lat2, location.Longitude + 180d / Math.PI * dLon);
}
}
}

View file

@ -1,5 +1,5 @@
// XAML Map Control - http://xamlmapcontrol.codeplex.com/ // XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2016 Clemens Fischer // © 2017 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL) // Licensed under the Microsoft Public License (Ms-PL)
using System; using System;
@ -10,9 +10,11 @@ using System.Net;
using System.Xml; using System.Xml;
#if NETFX_CORE #if NETFX_CORE
using Windows.UI.Xaml; using Windows.UI.Xaml;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Media.Imaging; using Windows.UI.Xaml.Media.Imaging;
#else #else
using System.Windows; using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging; using System.Windows.Media.Imaging;
#endif #endif
@ -20,8 +22,10 @@ namespace MapControl
{ {
/// <summary> /// <summary>
/// Displays Bing Maps tiles. The static ApiKey property must be set to a Bing Maps API Key. /// 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> /// </summary>
public class BingMapsTileLayer : TileLayer public class BingMapsTileLayer : MapTileLayer
{ {
public enum MapMode public enum MapMode
{ {
@ -45,6 +49,7 @@ namespace MapControl
public MapMode Mode { get; set; } public MapMode Mode { get; set; }
public string Culture { get; set; } public string Culture { get; set; }
public ImageSource LogoImage { get; set; }
private void OnLoaded(object sender, RoutedEventArgs e) private void OnLoaded(object sender, RoutedEventArgs e)
{ {
@ -52,13 +57,15 @@ namespace MapControl
if (string.IsNullOrEmpty(ApiKey)) if (string.IsNullOrEmpty(ApiKey))
{ {
throw new InvalidOperationException("BingMapsTileLayer requires a Bing Maps API Key."); 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);
var uri = string.Format("http://dev.virtualearth.net/REST/V1/Imagery/Metadata/{0}?output=xml&key={1}", Mode, ApiKey); request.BeginGetResponse(HandleImageryMetadataResponse, request);
var request = WebRequest.CreateHttp(uri); }
request.BeginGetResponse(HandleImageryMetadataResponse, request);
} }
private void HandleImageryMetadataResponse(IAsyncResult asyncResult) private void HandleImageryMetadataResponse(IAsyncResult asyncResult)
@ -122,7 +129,7 @@ namespace MapControl
if (!string.IsNullOrEmpty(imageUrl) && imageUrlSubdomains != null && imageUrlSubdomains.Length > 0) if (!string.IsNullOrEmpty(imageUrl) && imageUrlSubdomains != null && imageUrlSubdomains.Length > 0)
{ {
var _ = Dispatcher.BeginInvoke(new Action(() => var op = Dispatcher.BeginInvoke(new Action(() =>
{ {
if (string.IsNullOrEmpty(Culture)) if (string.IsNullOrEmpty(Culture))
{ {

View file

@ -1,5 +1,5 @@
// XAML Map Control - http://xamlmapcontrol.codeplex.com/ // XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2016 Clemens Fischer // © 2017 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL) // Licensed under the Microsoft Public License (Ms-PL)
using System; using System;

View file

@ -1,5 +1,5 @@
// XAML Map Control - http://xamlmapcontrol.codeplex.com/ // XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2016 Clemens Fischer // © 2017 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL) // Licensed under the Microsoft Public License (Ms-PL)
using System; using System;
@ -13,7 +13,7 @@ namespace MapControl
/// <summary> /// <summary>
/// Creates frozen BitmapSources from Stream or Uri. /// Creates frozen BitmapSources from Stream or Uri.
/// </summary> /// </summary>
internal static class ImageLoader public static class BitmapSourceHelper
{ {
public static BitmapSource FromStream(Stream stream) public static BitmapSource FromStream(Stream stream)
{ {
@ -30,19 +30,16 @@ namespace MapControl
public static BitmapSource FromUri(Uri uri) public static BitmapSource FromUri(Uri uri)
{ {
BitmapSource bitmap = null;
try try
{ {
var request = WebRequest.Create(uri); using (var response = WebRequest.Create(uri).GetResponse())
using (var response = request.GetResponse())
using (var responseStream = response.GetResponseStream()) using (var responseStream = response.GetResponseStream())
using (var memoryStream = new MemoryStream()) using (var memoryStream = new MemoryStream())
{ {
responseStream.CopyTo(memoryStream); responseStream.CopyTo(memoryStream);
memoryStream.Seek(0, SeekOrigin.Begin); memoryStream.Seek(0, SeekOrigin.Begin);
bitmap = FromStream(memoryStream);
return FromStream(memoryStream);
} }
} }
catch (Exception ex) catch (Exception ex)
@ -50,7 +47,7 @@ namespace MapControl
Debug.WriteLine(ex.Message); Debug.WriteLine(ex.Message);
} }
return bitmap; return null;
} }
} }
} }

92
MapControl/BoundingBox.cs Normal file
View file

@ -0,0 +1,92 @@
// 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.Globalization;
namespace MapControl
{
/// <summary>
/// A geographic bounding box with south and north latitude and west and east longitude values in degrees.
/// </summary>
public partial class BoundingBox
{
private double south;
private double west;
private double north;
private double east;
public BoundingBox()
{
}
public BoundingBox(double south, double west, double north, double east)
{
South = south;
West = west;
North = north;
East = east;
}
public double South
{
get { return south; }
set { south = Math.Min(Math.Max(value, -90d), 90d); }
}
public double West
{
get { return west; }
set { west = value; }
}
public double North
{
get { return north; }
set { north = Math.Min(Math.Max(value, -90d), 90d); }
}
public double East
{
get { return east; }
set { east = value; }
}
public virtual double Width
{
get { return east - west; }
}
public virtual double Height
{
get { return north - south; }
}
public bool HasValidBounds
{
get { return south < north && west < east; }
}
public virtual BoundingBox Clone()
{
return new BoundingBox(south, west, north, east);
}
public static BoundingBox Parse(string s)
{
var values = s.Split(new char[] { ',' });
if (values.Length != 4)
{
throw new FormatException("BoundingBox string must be a comma-separated list of four double values");
}
return new BoundingBox(
double.Parse(values[0], NumberStyles.Float, CultureInfo.InvariantCulture),
double.Parse(values[1], NumberStyles.Float, CultureInfo.InvariantCulture),
double.Parse(values[2], NumberStyles.Float, CultureInfo.InvariantCulture),
double.Parse(values[3], NumberStyles.Float, CultureInfo.InvariantCulture));
}
}
}

View file

@ -0,0 +1,31 @@
// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2017 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL)
using 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
{
}
}

View file

@ -0,0 +1,40 @@
// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2017 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL)
namespace MapControl
{
public class CenteredBoundingBox : BoundingBox
{
private readonly Location center;
private readonly double width;
private readonly double height;
public CenteredBoundingBox(Location center, double width, double height)
{
this.center = center;
this.width = width;
this.height = height;
}
public Location Center
{
get { return center; }
}
public override double Width
{
get { return width; }
}
public override double Height
{
get { return height; }
}
public override BoundingBox Clone()
{
return new CenteredBoundingBox(center, width, height);
}
}
}

View file

@ -0,0 +1,54 @@
// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2017 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL)
using System;
#if NETFX_CORE
using Windows.Foundation;
#else
using System.Windows;
#endif
namespace MapControl
{
/// <summary>
/// Transforms map coordinates according to the Equirectangular Projection.
/// Longitude and Latitude values are transformed identically to X and Y.
/// </summary>
public class EquirectangularProjection : MapProjection
{
public EquirectangularProjection()
: this("EPSG:4326")
{
}
public EquirectangularProjection(string crsId)
{
CrsId = crsId;
}
public override Point GetMapScale(Location location)
{
return new Point(
ViewportScale / (MetersPerDegree * Math.Cos(location.Latitude * Math.PI / 180d)),
ViewportScale / MetersPerDegree);
}
public override Point LocationToPoint(Location location)
{
return new Point(location.Longitude, location.Latitude);
}
public override Location PointToLocation(Point point)
{
return new Location(point.Y, point.X);
}
public override Location TranslateLocation(Location location, Point translation)
{
return new Location(
location.Latitude - translation.Y / ViewportScale,
location.Longitude + translation.X / ViewportScale);
}
}
}

View file

@ -1,5 +1,5 @@
// XAML Map Control - http://xamlmapcontrol.codeplex.com/ // XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2016 Clemens Fischer // © 2017 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL) // Licensed under the Microsoft Public License (Ms-PL)
using System.Windows; using System.Windows;

View file

@ -1,5 +1,5 @@
// XAML Map Control - http://xamlmapcontrol.codeplex.com/ // XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2016 Clemens Fischer // © 2017 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL) // Licensed under the Microsoft Public License (Ms-PL)
using System; using System;

View file

@ -0,0 +1,61 @@
// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2017 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL)
using System;
#if NETFX_CORE
using Windows.Foundation;
#else
using System.Windows;
#endif
namespace MapControl
{
/// <summary>
/// Transforms map coordinates according to the Gnomonic Projection.
/// </summary>
public class GnomonicProjection : AzimuthalProjection
{
public GnomonicProjection()
: this("AUTO2:97001") // GeoServer non-standard CRS ID
{
}
public GnomonicProjection(string crsId)
{
CrsId = crsId;
}
public override Point LocationToPoint(Location location)
{
if (location.Equals(projectionCenter))
{
return new Point();
}
double azimuth, distance;
GetAzimuthDistance(projectionCenter, location, out azimuth, out distance);
var mapDistance = distance < Math.PI / 2d
? Wgs84EquatorialRadius * Math.Tan(distance)
: double.PositiveInfinity;
return new Point(mapDistance * Math.Sin(azimuth), mapDistance * Math.Cos(azimuth));
}
public override Location PointToLocation(Point point)
{
if (point.X == 0d && point.Y == 0d)
{
return projectionCenter;
}
var azimuth = Math.Atan2(point.X, point.Y);
var mapDistance = Math.Sqrt(point.X * point.X + point.Y * point.Y);
var distance = Math.Atan(mapDistance / Wgs84EquatorialRadius);
return GetLocation(projectionCenter, azimuth, distance);
}
}
}

View file

@ -1,5 +1,5 @@
// XAML Map Control - http://xamlmapcontrol.codeplex.com/ // XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2016 Clemens Fischer // © 2017 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL) // Licensed under the Microsoft Public License (Ms-PL)
using System; using System;

View file

@ -1,11 +0,0 @@
// XAML Map Control - http://xamlmapcontrol.codeplex.com/
// © 2016 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL)
namespace MapControl
{
public interface IMapElement
{
MapBase ParentMap { get; set; }
}
}

View file

@ -1,14 +0,0 @@
// XAML Map Control - http://xamlmapcontrol.codeplex.com/
// © 2016 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL)
using System.Collections.Generic;
namespace MapControl
{
public interface ITileImageLoader
{
void BeginLoadTiles(TileLayer tileLayer, IEnumerable<Tile> tiles);
void CancelLoadTiles(TileLayer tileLayer);
}
}

View file

@ -1,5 +1,5 @@
// XAML Map Control - http://xamlmapcontrol.codeplex.com/ // XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2016 Clemens Fischer // © 2017 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL) // Licensed under the Microsoft Public License (Ms-PL)
using System; using System;

View file

@ -1,5 +1,5 @@
// XAML Map Control - http://xamlmapcontrol.codeplex.com/ // XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2016 Clemens Fischer // © 2017 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL) // Licensed under the Microsoft Public License (Ms-PL)
#if NETFX_CORE #if NETFX_CORE

View file

@ -1,5 +1,5 @@
// XAML Map Control - http://xamlmapcontrol.codeplex.com/ // XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2016 Clemens Fischer // © 2017 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL) // Licensed under the Microsoft Public License (Ms-PL)
using System.Windows.Media; using System.Windows.Media;
@ -28,7 +28,7 @@ namespace MapControl
{ {
if (IsAsync) if (IsAsync)
{ {
image = ImageLoader.FromUri(uri); image = BitmapSourceHelper.FromUri(uri);
} }
else else
{ {

View file

@ -1,5 +1,5 @@
// XAML Map Control - http://xamlmapcontrol.codeplex.com/ // XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2016 Clemens Fischer // © 2017 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL) // Licensed under the Microsoft Public License (Ms-PL)
using System; using System;
@ -62,16 +62,16 @@ namespace MapControl
public static Location Parse(string s) public static Location Parse(string s)
{ {
var pair = s.Split(new char[] { ',' }); var values = s.Split(new char[] { ',' });
if (pair.Length != 2) if (values.Length != 2)
{ {
throw new FormatException("Location string must be a comma-separated pair of double values"); throw new FormatException("Location string must be a comma-separated pair of double values");
} }
return new Location( return new Location(
double.Parse(pair[0], NumberStyles.Float, CultureInfo.InvariantCulture), double.Parse(values[0], NumberStyles.Float, CultureInfo.InvariantCulture),
double.Parse(pair[1], NumberStyles.Float, CultureInfo.InvariantCulture)); double.Parse(values[1], NumberStyles.Float, CultureInfo.InvariantCulture));
} }
/// <summary> /// <summary>

View file

@ -1,5 +1,5 @@
// XAML Map Control - http://xamlmapcontrol.codeplex.com/ // XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2016 Clemens Fischer // © 2017 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL) // Licensed under the Microsoft Public License (Ms-PL)
using System; using System;
@ -10,7 +10,7 @@ using System.Linq;
namespace MapControl namespace MapControl
{ {
/// <summary> /// <summary>
/// A collection of geographic locations. /// An ObservableCollection of Location with support for parsing.
/// </summary> /// </summary>
public partial class LocationCollection : ObservableCollection<Location> public partial class LocationCollection : ObservableCollection<Location>
{ {

View file

@ -1,5 +1,5 @@
// XAML Map Control - http://xamlmapcontrol.codeplex.com/ // XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2016 Clemens Fischer // © 2017 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL) // Licensed under the Microsoft Public License (Ms-PL)
using System; using System;

View file

@ -1,5 +1,5 @@
// XAML Map Control - http://xamlmapcontrol.codeplex.com/ // XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2016 Clemens Fischer // © 2017 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL) // Licensed under the Microsoft Public License (Ms-PL)
using System; using System;

View file

@ -1,5 +1,5 @@
// XAML Map Control - http://xamlmapcontrol.codeplex.com/ // XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2016 Clemens Fischer // © 2017 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL) // Licensed under the Microsoft Public License (Ms-PL)
using System.Windows; using System.Windows;
@ -13,7 +13,7 @@ namespace MapControl
public class Map : MapBase public class Map : MapBase
{ {
public static readonly DependencyProperty MouseWheelZoomDeltaProperty = DependencyProperty.Register( public static readonly DependencyProperty MouseWheelZoomDeltaProperty = DependencyProperty.Register(
"MouseWheelZoomDelta", typeof(double), typeof(Map), new PropertyMetadata(1d)); nameof(MouseWheelZoomDelta), typeof(double), typeof(Map), new PropertyMetadata(1d));
private Point? mousePosition; private Point? mousePosition;

View file

@ -1,5 +1,5 @@
// XAML Map Control - http://xamlmapcontrol.codeplex.com/ // XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2016 Clemens Fischer // © 2017 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL) // Licensed under the Microsoft Public License (Ms-PL)
using System.Windows; using System.Windows;
@ -13,10 +13,10 @@ namespace MapControl
public class Map : MapBase public class Map : MapBase
{ {
public static readonly DependencyProperty ManipulationModeProperty = DependencyProperty.Register( public static readonly DependencyProperty ManipulationModeProperty = DependencyProperty.Register(
"ManipulationMode", typeof(ManipulationModes), typeof(Map), new PropertyMetadata(ManipulationModes.All)); nameof(ManipulationMode), typeof(ManipulationModes), typeof(Map), new PropertyMetadata(ManipulationModes.All));
public static readonly DependencyProperty MouseWheelZoomDeltaProperty = DependencyProperty.Register( public static readonly DependencyProperty MouseWheelZoomDeltaProperty = DependencyProperty.Register(
"MouseWheelZoomDelta", typeof(double), typeof(Map), new PropertyMetadata(1d)); nameof(MouseWheelZoomDelta), typeof(double), typeof(Map), new PropertyMetadata(1d));
private Point? mousePosition; private Point? mousePosition;

View file

@ -1,5 +1,5 @@
// XAML Map Control - http://xamlmapcontrol.codeplex.com/ // XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2016 Clemens Fischer // © 2017 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL) // Licensed under the Microsoft Public License (Ms-PL)
using System; using System;
@ -15,7 +15,7 @@ namespace MapControl
public class Map : MapBase public class Map : MapBase
{ {
public static readonly DependencyProperty MouseWheelZoomDeltaProperty = DependencyProperty.Register( public static readonly DependencyProperty MouseWheelZoomDeltaProperty = DependencyProperty.Register(
"MouseWheelZoomDelta", typeof(double), typeof(Map), new PropertyMetadata(1d)); nameof(MouseWheelZoomDelta), typeof(double), typeof(Map), new PropertyMetadata(1d));
private bool transformPending; private bool transformPending;
private Point transformTranslation; private Point transformTranslation;

View file

@ -1,9 +1,7 @@
// XAML Map Control - http://xamlmapcontrol.codeplex.com/ // XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2016 Clemens Fischer // © 2017 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL) // Licensed under the Microsoft Public License (Ms-PL)
using System;
using System.Linq;
#if NETFX_CORE #if NETFX_CORE
using Windows.Foundation; using Windows.Foundation;
using Windows.UI; using Windows.UI;
@ -19,36 +17,36 @@ namespace MapControl
public partial class MapBase public partial class MapBase
{ {
public static readonly DependencyProperty ForegroundProperty = DependencyProperty.Register( public static readonly DependencyProperty ForegroundProperty = DependencyProperty.Register(
"Foreground", typeof(Brush), typeof(MapBase), nameof(Foreground), typeof(Brush), typeof(MapBase),
new PropertyMetadata(new SolidColorBrush(Colors.Black))); new PropertyMetadata(new SolidColorBrush(Colors.Black)));
public static readonly DependencyProperty CenterProperty = DependencyProperty.Register( public static readonly DependencyProperty CenterProperty = DependencyProperty.Register(
"Center", typeof(Location), typeof(MapBase), nameof(Center), typeof(Location), typeof(MapBase),
new PropertyMetadata(new Location(), (o, e) => ((MapBase)o).CenterPropertyChanged((Location)e.NewValue))); new PropertyMetadata(new Location(), (o, e) => ((MapBase)o).CenterPropertyChanged((Location)e.NewValue)));
public static readonly DependencyProperty TargetCenterProperty = DependencyProperty.Register( public static readonly DependencyProperty TargetCenterProperty = DependencyProperty.Register(
"TargetCenter", typeof(Location), typeof(MapBase), nameof(TargetCenter), typeof(Location), typeof(MapBase),
new PropertyMetadata(new Location(), (o, e) => ((MapBase)o).TargetCenterPropertyChanged((Location)e.NewValue))); new PropertyMetadata(new Location(), (o, e) => ((MapBase)o).TargetCenterPropertyChanged((Location)e.NewValue)));
public static readonly DependencyProperty ZoomLevelProperty = DependencyProperty.Register( public static readonly DependencyProperty ZoomLevelProperty = DependencyProperty.Register(
"ZoomLevel", typeof(double), typeof(MapBase), nameof(ZoomLevel), typeof(double), typeof(MapBase),
new PropertyMetadata(1d, (o, e) => ((MapBase)o).ZoomLevelPropertyChanged((double)e.NewValue))); new PropertyMetadata(1d, (o, e) => ((MapBase)o).ZoomLevelPropertyChanged((double)e.NewValue)));
public static readonly DependencyProperty TargetZoomLevelProperty = DependencyProperty.Register( public static readonly DependencyProperty TargetZoomLevelProperty = DependencyProperty.Register(
"TargetZoomLevel", typeof(double), typeof(MapBase), nameof(TargetZoomLevel), typeof(double), typeof(MapBase),
new PropertyMetadata(1d, (o, e) => ((MapBase)o).TargetZoomLevelPropertyChanged((double)e.NewValue))); new PropertyMetadata(1d, (o, e) => ((MapBase)o).TargetZoomLevelPropertyChanged((double)e.NewValue)));
public static readonly DependencyProperty HeadingProperty = DependencyProperty.Register( public static readonly DependencyProperty HeadingProperty = DependencyProperty.Register(
"Heading", typeof(double), typeof(MapBase), nameof(Heading), typeof(double), typeof(MapBase),
new PropertyMetadata(0d, (o, e) => ((MapBase)o).HeadingPropertyChanged((double)e.NewValue))); new PropertyMetadata(0d, (o, e) => ((MapBase)o).HeadingPropertyChanged((double)e.NewValue)));
public static readonly DependencyProperty TargetHeadingProperty = DependencyProperty.Register( public static readonly DependencyProperty TargetHeadingProperty = DependencyProperty.Register(
"TargetHeading", typeof(double), typeof(MapBase), nameof(TargetHeading), typeof(double), typeof(MapBase),
new PropertyMetadata(0d, (o, e) => ((MapBase)o).TargetHeadingPropertyChanged((double)e.NewValue))); new PropertyMetadata(0d, (o, e) => ((MapBase)o).TargetHeadingPropertyChanged((double)e.NewValue)));
partial void Initialize() partial void Initialize()
{ {
// set Background by Style to enable resetting by ClearValue in RemoveTileLayers // set Background by Style to enable resetting by ClearValue in MapLayerPropertyChanged
var style = new Style(typeof(MapBase)); var style = new Style(typeof(MapBase));
style.Setters.Add(new Setter(BackgroundProperty, new SolidColorBrush(Colors.Transparent))); style.Setters.Add(new Setter(BackgroundProperty, new SolidColorBrush(Colors.Transparent)));
Style = style; Style = style;
@ -62,28 +60,10 @@ namespace MapControl
{ {
clip.Rect = new Rect(0d, 0d, e.NewSize.Width, e.NewSize.Height); clip.Rect = new Rect(0d, 0d, e.NewSize.Width, e.NewSize.Height);
ResetTransformOrigin(); ResetTransformCenter();
UpdateTransform(); UpdateTransform();
} }
}; };
} }
private void SetViewportTransform(Location origin)
{
MapOrigin = mapTransform.Transform(origin);
ViewportScale = Math.Pow(2d, ZoomLevel) * TileSource.TileSize / 360d;
var transform = new Matrix(1d, 0d, 0d, 1d, -MapOrigin.X, -MapOrigin.Y)
.Rotate(-Heading)
.Scale(ViewportScale, -ViewportScale)
.Translate(ViewportOrigin.X, ViewportOrigin.Y);
viewportTransform.Matrix = transform;
}
private void SetTileLayer(TileLayer tileLayer)
{
TileLayer = tileLayer;
}
} }
} }

View file

@ -1,8 +1,7 @@
// XAML Map Control - http://xamlmapcontrol.codeplex.com/ // XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2016 Clemens Fischer // © 2017 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL) // Licensed under the Microsoft Public License (Ms-PL)
using System;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Media; using System.Windows.Media;
@ -15,42 +14,39 @@ namespace MapControl
Control.ForegroundProperty.AddOwner(typeof(MapBase)); Control.ForegroundProperty.AddOwner(typeof(MapBase));
public static readonly DependencyProperty CenterProperty = DependencyProperty.Register( public static readonly DependencyProperty CenterProperty = DependencyProperty.Register(
"Center", typeof(Location), typeof(MapBase), new FrameworkPropertyMetadata( nameof(Center), typeof(Location), typeof(MapBase), new FrameworkPropertyMetadata(
new Location(), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, new Location(), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
(o, e) => ((MapBase)o).CenterPropertyChanged((Location)e.NewValue))); (o, e) => ((MapBase)o).CenterPropertyChanged((Location)e.NewValue)));
public static readonly DependencyProperty TargetCenterProperty = DependencyProperty.Register( public static readonly DependencyProperty TargetCenterProperty = DependencyProperty.Register(
"TargetCenter", typeof(Location), typeof(MapBase), new FrameworkPropertyMetadata( nameof(TargetCenter), typeof(Location), typeof(MapBase), new FrameworkPropertyMetadata(
new Location(), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, new Location(), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
(o, e) => ((MapBase)o).TargetCenterPropertyChanged((Location)e.NewValue))); (o, e) => ((MapBase)o).TargetCenterPropertyChanged((Location)e.NewValue)));
public static readonly DependencyProperty ZoomLevelProperty = DependencyProperty.Register( public static readonly DependencyProperty ZoomLevelProperty = DependencyProperty.Register(
"ZoomLevel", typeof(double), typeof(MapBase), new FrameworkPropertyMetadata( nameof(ZoomLevel), typeof(double), typeof(MapBase), new FrameworkPropertyMetadata(
1d, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, 1d, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
(o, e) => ((MapBase)o).ZoomLevelPropertyChanged((double)e.NewValue))); (o, e) => ((MapBase)o).ZoomLevelPropertyChanged((double)e.NewValue)));
public static readonly DependencyProperty TargetZoomLevelProperty = DependencyProperty.Register( public static readonly DependencyProperty TargetZoomLevelProperty = DependencyProperty.Register(
"TargetZoomLevel", typeof(double), typeof(MapBase), new FrameworkPropertyMetadata( nameof(TargetZoomLevel), typeof(double), typeof(MapBase), new FrameworkPropertyMetadata(
1d, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, 1d, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
(o, e) => ((MapBase)o).TargetZoomLevelPropertyChanged((double)e.NewValue))); (o, e) => ((MapBase)o).TargetZoomLevelPropertyChanged((double)e.NewValue)));
public static readonly DependencyProperty HeadingProperty = DependencyProperty.Register( public static readonly DependencyProperty HeadingProperty = DependencyProperty.Register(
"Heading", typeof(double), typeof(MapBase), new FrameworkPropertyMetadata( nameof(Heading), typeof(double), typeof(MapBase), new FrameworkPropertyMetadata(
0d, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, 0d, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
(o, e) => ((MapBase)o).HeadingPropertyChanged((double)e.NewValue))); (o, e) => ((MapBase)o).HeadingPropertyChanged((double)e.NewValue)));
public static readonly DependencyProperty TargetHeadingProperty = DependencyProperty.Register( public static readonly DependencyProperty TargetHeadingProperty = DependencyProperty.Register(
"TargetHeading", typeof(double), typeof(MapBase), new FrameworkPropertyMetadata( nameof(TargetHeading), typeof(double), typeof(MapBase), new FrameworkPropertyMetadata(
0d, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, 0d, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
(o, e) => ((MapBase)o).TargetHeadingPropertyChanged((double)e.NewValue))); (o, e) => ((MapBase)o).TargetHeadingPropertyChanged((double)e.NewValue)));
static MapBase() static MapBase()
{ {
ClipToBoundsProperty.OverrideMetadata( ClipToBoundsProperty.OverrideMetadata(typeof(MapBase), new FrameworkPropertyMetadata(true));
typeof(MapBase), new FrameworkPropertyMetadata(true)); BackgroundProperty.OverrideMetadata(typeof(MapBase), new FrameworkPropertyMetadata(Brushes.Transparent));
BackgroundProperty.OverrideMetadata(
typeof(MapBase), new FrameworkPropertyMetadata(Brushes.Transparent));
} }
partial void RemoveAnimation(DependencyProperty property) partial void RemoveAnimation(DependencyProperty property)
@ -69,37 +65,19 @@ namespace MapControl
/// <summary> /// <summary>
/// Changes the Center, Heading and ZoomLevel properties according to the specified /// Changes the Center, Heading and ZoomLevel properties according to the specified
/// viewport coordinate translation, rotation and scale delta values. Rotation and scaling /// viewport coordinate translation, rotation and scale delta values. Rotation and scaling
/// is performed relative to the specified origin point in viewport coordinates. /// is performed relative to the specified center point in viewport coordinates.
/// </summary> /// </summary>
public void TransformMap(Point origin, Vector translation, double rotation, double scale) public void TransformMap(Point center, Vector translation, double rotation, double scale)
{ {
TransformMap(origin, (Point)translation, rotation, scale); TransformMap(center, (Point)translation, rotation, scale);
} }
protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo) protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
{ {
base.OnRenderSizeChanged(sizeInfo); base.OnRenderSizeChanged(sizeInfo);
ResetTransformOrigin(); ResetTransformCenter();
UpdateTransform(); UpdateTransform();
} }
private void SetViewportTransform(Location origin)
{
MapOrigin = mapTransform.Transform(origin);
ViewportScale = Math.Pow(2d, ZoomLevel) * TileSource.TileSize / 360d;
var transform = new Matrix(1d, 0d, 0d, 1d, -MapOrigin.X, -MapOrigin.Y);
transform.Rotate(-Heading);
transform.Scale(ViewportScale, -ViewportScale);
transform.Translate(ViewportOrigin.X, ViewportOrigin.Y);
viewportTransform.Matrix = transform;
}
private void SetTileLayer(TileLayer tileLayer)
{
SetCurrentValue(TileLayerProperty, tileLayer);
}
} }
} }

View file

@ -1,12 +1,8 @@
// XAML Map Control - http://xamlmapcontrol.codeplex.com/ // XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2016 Clemens Fischer // © 2017 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL) // Licensed under the Microsoft Public License (Ms-PL)
using System; using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Linq;
#if NETFX_CORE #if NETFX_CORE
using Windows.Foundation; using Windows.Foundation;
using Windows.UI.Xaml; using Windows.UI.Xaml;
@ -20,8 +16,14 @@ using System.Windows.Media.Animation;
namespace MapControl namespace MapControl
{ {
public interface IMapLayer : IMapElement
{
Brush MapBackground { get; }
Brush MapForeground { get; }
}
/// <summary> /// <summary>
/// The map control. Displays map content provided by the TileLayer or TileLayers property. /// The map control. Displays map content provided by one or more MapTileLayers or MapImageLayers.
/// The visible map area is defined by the Center and ZoomLevel properties. /// The visible map area is defined by the Center and ZoomLevel properties.
/// The map can be rotated by an angle that is given by the Heading property. /// The map can be rotated by an angle that is given by the Heading property.
/// MapBase can contain map overlay child elements like other MapPanels or MapItemsControls. /// MapBase can contain map overlay child elements like other MapPanels or MapItemsControls.
@ -30,60 +32,57 @@ namespace MapControl
{ {
private const double MaximumZoomLevel = 22d; private const double MaximumZoomLevel = 22d;
public static readonly DependencyProperty TileLayerProperty = DependencyProperty.Register( public static readonly DependencyProperty MapLayerProperty = DependencyProperty.Register(
"TileLayer", typeof(TileLayer), typeof(MapBase), nameof(MapLayer), typeof(UIElement), typeof(MapBase),
new PropertyMetadata(null, (o, e) => ((MapBase)o).TileLayerPropertyChanged((TileLayer)e.NewValue))); new PropertyMetadata(null, (o, e) => ((MapBase)o).MapLayerPropertyChanged((UIElement)e.OldValue, (UIElement)e.NewValue)));
public static readonly DependencyProperty TileLayersProperty = DependencyProperty.Register( public static readonly DependencyProperty MapProjectionProperty = DependencyProperty.Register(
"TileLayers", typeof(IList<TileLayer>), typeof(MapBase), nameof(MapProjection), typeof(MapProjection), typeof(MapBase),
new PropertyMetadata(null, (o, e) => ((MapBase)o).TileLayersPropertyChanged((IList<TileLayer>)e.OldValue, (IList<TileLayer>)e.NewValue))); new PropertyMetadata(null, (o, e) => ((MapBase)o).MapProjectionPropertyChanged()));
public static readonly DependencyProperty ProjectionCenterProperty = DependencyProperty.Register(
nameof(ProjectionCenter), typeof(Location), typeof(MapBase),
new PropertyMetadata(null, (o, e) => ((MapBase)o).ProjectionCenterPropertyChanged()));
public static readonly DependencyProperty MinZoomLevelProperty = DependencyProperty.Register( public static readonly DependencyProperty MinZoomLevelProperty = DependencyProperty.Register(
"MinZoomLevel", typeof(double), typeof(MapBase), nameof(MinZoomLevel), typeof(double), typeof(MapBase),
new PropertyMetadata(1d, (o, e) => ((MapBase)o).MinZoomLevelPropertyChanged((double)e.NewValue))); new PropertyMetadata(1d, (o, e) => ((MapBase)o).MinZoomLevelPropertyChanged((double)e.NewValue)));
public static readonly DependencyProperty MaxZoomLevelProperty = DependencyProperty.Register( public static readonly DependencyProperty MaxZoomLevelProperty = DependencyProperty.Register(
"MaxZoomLevel", typeof(double), typeof(MapBase), nameof(MaxZoomLevel), typeof(double), typeof(MapBase),
new PropertyMetadata(19d, (o, e) => ((MapBase)o).MaxZoomLevelPropertyChanged((double)e.NewValue))); new PropertyMetadata(19d, (o, e) => ((MapBase)o).MaxZoomLevelPropertyChanged((double)e.NewValue)));
public static readonly DependencyProperty AnimationDurationProperty = DependencyProperty.Register( public static readonly DependencyProperty AnimationDurationProperty = DependencyProperty.Register(
"AnimationDuration", typeof(TimeSpan), typeof(MapBase), nameof(AnimationDuration), typeof(TimeSpan), typeof(MapBase),
new PropertyMetadata(TimeSpan.FromSeconds(0.3))); new PropertyMetadata(TimeSpan.FromSeconds(0.3)));
public static readonly DependencyProperty AnimationEasingFunctionProperty = DependencyProperty.Register( public static readonly DependencyProperty AnimationEasingFunctionProperty = DependencyProperty.Register(
"AnimationEasingFunction", typeof(EasingFunctionBase), typeof(MapBase), nameof(AnimationEasingFunction), typeof(EasingFunctionBase), typeof(MapBase),
new PropertyMetadata(new QuadraticEase { EasingMode = EasingMode.EaseOut })); new PropertyMetadata(new QuadraticEase { EasingMode = EasingMode.EaseOut }));
public static readonly DependencyProperty TileFadeDurationProperty = DependencyProperty.Register( public static readonly DependencyProperty TileFadeDurationProperty = DependencyProperty.Register(
"TileFadeDuration", typeof(TimeSpan), typeof(MapBase), nameof(TileFadeDuration), typeof(TimeSpan), typeof(MapBase),
new PropertyMetadata(Tile.FadeDuration, (o, e) => Tile.FadeDuration = (TimeSpan)e.NewValue)); new PropertyMetadata(Tile.FadeDuration, (o, e) => Tile.FadeDuration = (TimeSpan)e.NewValue));
internal static readonly DependencyProperty CenterPointProperty = DependencyProperty.Register( internal static readonly DependencyProperty CenterPointProperty = DependencyProperty.Register(
"CenterPoint", typeof(Point), typeof(MapBase), "CenterPoint", typeof(Point), typeof(MapBase),
new PropertyMetadata(new Point(), (o, e) => ((MapBase)o).CenterPointPropertyChanged((Point)e.NewValue))); new PropertyMetadata(new Point(), (o, e) => ((MapBase)o).CenterPointPropertyChanged((Point)e.NewValue)));
private readonly PanelBase tileLayerPanel = new PanelBase();
private readonly MapTransform mapTransform = new MercatorTransform();
private readonly MatrixTransform viewportTransform = new MatrixTransform();
private readonly ScaleTransform scaleTransform = new ScaleTransform();
private readonly RotateTransform rotateTransform = new RotateTransform();
private readonly TransformGroup scaleRotateTransform = new TransformGroup();
private Location transformOrigin;
private PointAnimation centerAnimation; private PointAnimation centerAnimation;
private DoubleAnimation zoomLevelAnimation; private DoubleAnimation zoomLevelAnimation;
private DoubleAnimation headingAnimation; private DoubleAnimation headingAnimation;
private Location transformCenter;
private Point viewportCenter;
private double centerLongitude;
private bool internalPropertyChange; private bool internalPropertyChange;
public MapBase() public MapBase()
{ {
Initialize(); Initialize();
scaleRotateTransform.Children.Add(scaleTransform); MapProjection = new WebMercatorProjection();
scaleRotateTransform.Children.Add(rotateTransform); ScaleRotateTransform.Children.Add(ScaleTransform);
ScaleRotateTransform.Children.Add(RotateTransform);
Children.Add(tileLayerPanel);
TileLayers = new ObservableCollection<TileLayer>();
} }
partial void Initialize(); // Windows Runtime and Silverlight only partial void Initialize(); // Windows Runtime and Silverlight only
@ -104,27 +103,37 @@ namespace MapControl
} }
/// <summary> /// <summary>
/// Gets or sets the base TileLayer used by the Map control. /// Gets or sets the base map layer, which is added as first element to the Children collection.
/// If the layer implements IMapLayer (like MapTileLayer or MapImageLayer), its (non-null) MapBackground
/// and MapForeground property values are used for the MapBase Background and Foreground properties.
/// </summary> /// </summary>
public TileLayer TileLayer public UIElement MapLayer
{ {
get { return (TileLayer)GetValue(TileLayerProperty); } get { return (UIElement)GetValue(MapLayerProperty); }
set { SetValue(TileLayerProperty, value); } set { SetValue(MapLayerProperty, value); }
} }
/// <summary> /// <summary>
/// Gets or sets optional multiple TileLayers that are used simultaneously. /// Gets or sets the MapProjection used by the map control.
/// The first element in the collection is equal to the value of the TileLayer
/// property. The additional TileLayers usually have transparent backgrounds.
/// </summary> /// </summary>
public IList<TileLayer> TileLayers public MapProjection MapProjection
{ {
get { return (IList<TileLayer>)GetValue(TileLayersProperty); } get { return (MapProjection)GetValue(MapProjectionProperty); }
set { SetValue(TileLayersProperty, value); } set { SetValue(MapProjectionProperty, value); }
} }
/// <summary> /// <summary>
/// Gets or sets the location of the center point of the Map. /// Gets or sets an optional center (reference point) for azimuthal projections.
/// If ProjectionCenter is null, the Center property value will be used instead.
/// </summary>
public Location ProjectionCenter
{
get { return (Location)GetValue(ProjectionCenterProperty); }
set { SetValue(ProjectionCenterProperty, value); }
}
/// <summary>
/// Gets or sets the location of the center point of the map.
/// </summary> /// </summary>
public Location Center public Location Center
{ {
@ -229,111 +238,54 @@ namespace MapControl
set { SetValue(TileFadeDurationProperty, value); } set { SetValue(TileFadeDurationProperty, value); }
} }
/// <summary>
/// Gets the transformation from geographic coordinates to cartesian map coordinates.
/// </summary>
public MapTransform MapTransform
{
get { return mapTransform; }
}
/// <summary>
/// Gets the transformation from cartesian map coordinates to viewport coordinates (i.e. pixels).
/// </summary>
public MatrixTransform ViewportTransform
{
get { return viewportTransform; }
}
/// <summary> /// <summary>
/// Gets the scaling transformation from meters to viewport coordinate units at the Center location. /// Gets the scaling transformation from meters to viewport coordinate units at the Center location.
/// </summary> /// </summary>
public ScaleTransform ScaleTransform public ScaleTransform ScaleTransform { get; } = new ScaleTransform();
{
get { return scaleTransform; }
}
/// <summary> /// <summary>
/// Gets the transformation that rotates by the value of the Heading property. /// Gets the transformation that rotates by the value of the Heading property.
/// </summary> /// </summary>
public RotateTransform RotateTransform public RotateTransform RotateTransform { get; } = new RotateTransform();
{
get { return rotateTransform; }
}
/// <summary> /// <summary>
/// Gets the combination of ScaleTransform and RotateTransform /// Gets the combination of ScaleTransform and RotateTransform
/// </summary> /// </summary>
public TransformGroup ScaleRotateTransform public TransformGroup ScaleRotateTransform { get; } = new TransformGroup();
{
get { return scaleRotateTransform; }
}
internal Point MapOrigin { get; private set; }
internal Point ViewportOrigin { get; private set; }
/// <summary> /// <summary>
/// Gets the scaling factor from cartesian map coordinates to viewport coordinates. /// Transforms a Location in geographic coordinates to a Point in viewport coordinates.
/// </summary>
public double ViewportScale { get; private set; }
/// <summary>
/// Gets the scaling factor from meters to viewport coordinate units at the Center location (px/m).
/// </summary>
public double CenterScale { get; private set; }
/// <summary>
/// Gets the map scale at the specified location as viewport coordinate units per meter (px/m).
/// </summary>
public double GetMapScale(Location location)
{
return mapTransform.RelativeScale(location) *
Math.Pow(2d, ZoomLevel) * TileSource.TileSize / (TileSource.MetersPerDegree * 360d);
}
/// <summary>
/// Transforms a geographic location to a viewport coordinates point.
/// </summary> /// </summary>
public Point LocationToViewportPoint(Location location) public Point LocationToViewportPoint(Location location)
{ {
return viewportTransform.Transform(mapTransform.Transform(location)); return MapProjection.LocationToViewportPoint(location);
} }
/// <summary> /// <summary>
/// Transforms a viewport coordinates point to a geographic location. /// Transforms a Point in viewport coordinates to a Location in geographic coordinates.
/// </summary> /// </summary>
public Location ViewportPointToLocation(Point point) public Location ViewportPointToLocation(Point point)
{ {
return mapTransform.Transform(viewportTransform.Inverse.Transform(point)); return MapProjection.ViewportPointToLocation(point);
} }
/// <summary> /// <summary>
/// Sets a temporary origin location in geographic coordinates for scaling and rotation transformations. /// Sets a temporary center point in viewport coordinates for scaling and rotation transformations.
/// This origin location is automatically reset when the Center property is set by application code. /// This center point is automatically reset when the Center property is set by application code.
/// </summary> /// </summary>
public void SetTransformOrigin(Location origin) public void SetTransformCenter(Point center)
{ {
transformOrigin = origin; transformCenter = MapProjection.ViewportPointToLocation(center);
ViewportOrigin = LocationToViewportPoint(origin); viewportCenter = center;
} }
/// <summary> /// <summary>
/// Sets a temporary origin point in viewport coordinates for scaling and rotation transformations. /// Resets the temporary transform center point set by SetTransformCenter.
/// This origin point is automatically reset when the Center property is set by application code.
/// </summary> /// </summary>
public void SetTransformOrigin(Point origin) public void ResetTransformCenter()
{ {
transformOrigin = ViewportPointToLocation(origin); transformCenter = null;
ViewportOrigin = origin; viewportCenter = new Point(RenderSize.Width / 2d, RenderSize.Height / 2d);
}
/// <summary>
/// Resets the temporary transform origin point set by SetTransformOrigin.
/// </summary>
public void ResetTransformOrigin()
{
transformOrigin = null;
ViewportOrigin = new Point(RenderSize.Width / 2d, RenderSize.Height / 2d);
} }
/// <summary> /// <summary>
@ -341,9 +293,9 @@ namespace MapControl
/// </summary> /// </summary>
public void TranslateMap(Point translation) public void TranslateMap(Point translation)
{ {
if (transformOrigin != null) if (transformCenter != null)
{ {
ResetTransformOrigin(); ResetTransformCenter();
UpdateTransform(); UpdateTransform();
} }
@ -359,24 +311,24 @@ namespace MapControl
translation.Y * cos - translation.X * sin); translation.Y * cos - translation.X * sin);
} }
translation.X /= -ViewportScale; translation.X = -translation.X;
translation.Y /= ViewportScale; translation.Y = -translation.Y;
Center = mapTransform.Transform(Center, MapOrigin, translation); Center = MapProjection.TranslateLocation(Center, translation);
} }
} }
/// <summary> /// <summary>
/// Changes the Center, Heading and ZoomLevel properties according to the specified /// Changes the Center, Heading and ZoomLevel properties according to the specified
/// viewport coordinate translation, rotation and scale delta values. Rotation and scaling /// viewport coordinate translation, rotation and scale delta values. Rotation and scaling
/// is performed relative to the specified origin point in viewport coordinates. /// is performed relative to the specified center point in viewport coordinates.
/// </summary> /// </summary>
public void TransformMap(Point origin, Point translation, double rotation, double scale) public void TransformMap(Point center, Point translation, double rotation, double scale)
{ {
if (rotation != 0d || scale != 1d) if (rotation != 0d || scale != 1d)
{ {
transformOrigin = ViewportPointToLocation(origin); transformCenter = MapProjection.ViewportPointToLocation(center);
ViewportOrigin = new Point(origin.X + translation.X, origin.Y + translation.Y); viewportCenter = new Point(center.X + translation.X, center.Y + translation.Y);
if (rotation != 0d) if (rotation != 0d)
{ {
@ -401,175 +353,104 @@ namespace MapControl
} }
/// <summary> /// <summary>
/// Sets the value of the TargetZoomLevel property while retaining the specified origin point /// Sets the value of the TargetZoomLevel property while retaining the specified center point
/// in viewport coordinates. /// in viewport coordinates.
/// </summary> /// </summary>
public void ZoomMap(Point origin, double zoomLevel) public void ZoomMap(Point center, double zoomLevel)
{ {
zoomLevel = Math.Min(Math.Max(zoomLevel, MinZoomLevel), MaxZoomLevel); zoomLevel = Math.Min(Math.Max(zoomLevel, MinZoomLevel), MaxZoomLevel);
if (TargetZoomLevel != zoomLevel) if (TargetZoomLevel != zoomLevel)
{ {
SetTransformOrigin(origin); SetTransformCenter(center);
TargetZoomLevel = zoomLevel;
if (double.IsNaN(MapProjection.LongitudeScale))
{
ZoomLevel = zoomLevel;
}
else
{
TargetZoomLevel = zoomLevel;
}
} }
} }
/// <summary> /// <summary>
/// Sets the TargetZoomLevel and TargetCenter properties such that the specified bounding box /// Sets the TargetZoomLevel and TargetCenter properties so that the specified bounding box
/// fits into the current viewport. The TargetHeading property is set to zero. /// fits into the current viewport. The TargetHeading property is set to zero.
/// </summary> /// </summary>
public void ZoomToBounds(Location southWest, Location northEast) public void ZoomToBounds(BoundingBox boundingBox)
{ {
if (southWest.Latitude < northEast.Latitude && southWest.Longitude < northEast.Longitude) if (boundingBox != null && boundingBox.HasValidBounds)
{ {
var p1 = mapTransform.Transform(southWest); var rect = MapProjection.BoundingBoxToRect(boundingBox);
var p2 = mapTransform.Transform(northEast); var center = new Point(rect.X + rect.Width / 2d, rect.Y + rect.Height / 2d);
var lonScale = RenderSize.Width / (p2.X - p1.X) * 360d / TileSource.TileSize; var scale0 = 1d / MapProjection.GetViewportScale(0d);
var latScale = RenderSize.Height / (p2.Y - p1.Y) * 360d / TileSource.TileSize; var lonScale = scale0 * RenderSize.Width / rect.Width;
var latScale = scale0 * RenderSize.Height / rect.Height;
var lonZoom = Math.Log(lonScale, 2d); var lonZoom = Math.Log(lonScale, 2d);
var latZoom = Math.Log(latScale, 2d); var latZoom = Math.Log(latScale, 2d);
TargetZoomLevel = Math.Min(lonZoom, latZoom); TargetZoomLevel = Math.Min(lonZoom, latZoom);
TargetCenter = mapTransform.Transform(new Point((p1.X + p2.X) / 2d, (p1.Y + p2.Y) / 2d)); TargetCenter = MapProjection.PointToLocation(center);
TargetHeading = 0d; TargetHeading = 0d;
} }
} }
private void TileLayerPropertyChanged(TileLayer tileLayer) private void MapLayerPropertyChanged(UIElement oldLayer, UIElement newLayer)
{ {
if (tileLayer != null) if (oldLayer != null)
{ {
if (TileLayers == null) Children.Remove(oldLayer);
var mapLayer = oldLayer as IMapLayer;
if (mapLayer != null)
{ {
TileLayers = new ObservableCollection<TileLayer>(new TileLayer[] { tileLayer }); if (mapLayer.MapBackground != null)
}
else if (TileLayers.Count == 0)
{
TileLayers.Add(tileLayer);
}
else if (TileLayers[0] != tileLayer)
{
TileLayers[0] = tileLayer;
}
}
}
private void TileLayersPropertyChanged(IList<TileLayer> oldTileLayers, IList<TileLayer> newTileLayers)
{
if (oldTileLayers != null)
{
var oldCollection = oldTileLayers as INotifyCollectionChanged;
if (oldCollection != null)
{
oldCollection.CollectionChanged -= TileLayerCollectionChanged;
}
SetTileLayer(null);
ClearTileLayers();
}
if (newTileLayers != null)
{
SetTileLayer(newTileLayers.FirstOrDefault());
AddTileLayers(0, newTileLayers);
var newCollection = newTileLayers as INotifyCollectionChanged;
if (newCollection != null)
{
newCollection.CollectionChanged += TileLayerCollectionChanged;
}
}
}
private void TileLayerCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
AddTileLayers(e.NewStartingIndex, e.NewItems.Cast<TileLayer>());
break;
case NotifyCollectionChangedAction.Remove:
RemoveTileLayers(e.OldStartingIndex, e.OldItems.Count);
break;
#if !SILVERLIGHT
case NotifyCollectionChangedAction.Move:
#endif
case NotifyCollectionChangedAction.Replace:
RemoveTileLayers(e.NewStartingIndex, e.OldItems.Count);
AddTileLayers(e.NewStartingIndex, e.NewItems.Cast<TileLayer>());
break;
case NotifyCollectionChangedAction.Reset:
ClearTileLayers();
if (e.NewItems != null)
{ {
AddTileLayers(0, e.NewItems.Cast<TileLayer>()); ClearValue(BackgroundProperty);
} }
break; if (mapLayer.MapForeground != null)
default:
break;
}
var tileLayer = TileLayers.FirstOrDefault();
if (TileLayer != tileLayer)
{
SetTileLayer(tileLayer);
}
}
private void AddTileLayers(int index, IEnumerable<TileLayer> tileLayers)
{
foreach (var tileLayer in tileLayers)
{
if (index == 0)
{
if (tileLayer.Background != null)
{ {
Background = tileLayer.Background; ClearValue(ForegroundProperty);
}
if (tileLayer.Foreground != null)
{
Foreground = tileLayer.Foreground;
} }
} }
tileLayerPanel.Children.Insert(index++, tileLayer);
} }
}
private void RemoveTileLayers(int index, int count) if (newLayer != null)
{
while (count-- > 0)
{ {
tileLayerPanel.Children.RemoveAt(index + count); Children.Insert(0, newLayer);
}
if (index == 0) var mapLayer = newLayer as IMapLayer;
if (mapLayer != null)
{
if (mapLayer.MapBackground != null)
{
Background = mapLayer.MapBackground;
}
if (mapLayer.MapForeground != null)
{
Foreground = mapLayer.MapForeground;
}
}
}
}
private void MapProjectionPropertyChanged()
{
ResetTransformCenter();
UpdateTransform(false, true);
}
private void ProjectionCenterPropertyChanged()
{
if (MapProjection.IsAzimuthal)
{ {
ClearValue(BackgroundProperty); ResetTransformCenter();
ClearValue(ForegroundProperty); UpdateTransform();
} }
} }
private void ClearTileLayers()
{
tileLayerPanel.Children.Clear();
ClearValue(BackgroundProperty);
ClearValue(ForegroundProperty);
}
private void InternalSetValue(DependencyProperty property, object value)
{
internalPropertyChange = true;
SetValue(property, value);
internalPropertyChange = false;
}
private void AdjustCenterProperty(DependencyProperty property, ref Location center) private void AdjustCenterProperty(DependencyProperty property, ref Location center)
{ {
if (center == null) if (center == null)
@ -578,10 +459,10 @@ namespace MapControl
InternalSetValue(property, center); InternalSetValue(property, center);
} }
else if (center.Longitude < -180d || center.Longitude > 180d || else if (center.Longitude < -180d || center.Longitude > 180d ||
center.Latitude < -mapTransform.MaxLatitude || center.Latitude > mapTransform.MaxLatitude) center.Latitude < -MapProjection.MaxLatitude || center.Latitude > MapProjection.MaxLatitude)
{ {
center = new Location( center = new Location(
Math.Min(Math.Max(center.Latitude, -mapTransform.MaxLatitude), mapTransform.MaxLatitude), Math.Min(Math.Max(center.Latitude, -MapProjection.MaxLatitude), MapProjection.MaxLatitude),
Location.NormalizeLongitude(center.Longitude)); Location.NormalizeLongitude(center.Longitude));
InternalSetValue(property, center); InternalSetValue(property, center);
} }
@ -597,7 +478,7 @@ namespace MapControl
if (centerAnimation == null) if (centerAnimation == null)
{ {
InternalSetValue(TargetCenterProperty, center); InternalSetValue(TargetCenterProperty, center);
InternalSetValue(CenterPointProperty, mapTransform.Transform(center)); InternalSetValue(CenterPointProperty, MapProjection.LocationToPoint(center));
} }
} }
} }
@ -618,8 +499,8 @@ namespace MapControl
// animate private CenterPoint property by PointAnimation // animate private CenterPoint property by PointAnimation
centerAnimation = new PointAnimation centerAnimation = new PointAnimation
{ {
From = mapTransform.Transform(Center), From = MapProjection.LocationToPoint(Center),
To = mapTransform.Transform(new Location( To = MapProjection.LocationToPoint(new Location(
targetCenter.Latitude, targetCenter.Latitude,
Location.NearestLongitude(targetCenter.Longitude, Center.Longitude))), Location.NearestLongitude(targetCenter.Longitude, Center.Longitude))),
Duration = AnimationDuration, Duration = AnimationDuration,
@ -640,7 +521,7 @@ namespace MapControl
centerAnimation = null; centerAnimation = null;
InternalSetValue(CenterProperty, TargetCenter); InternalSetValue(CenterProperty, TargetCenter);
InternalSetValue(CenterPointProperty, mapTransform.Transform(TargetCenter)); InternalSetValue(CenterPointProperty, MapProjection.LocationToPoint(TargetCenter));
RemoveAnimation(CenterPointProperty); // remove holding animation in WPF RemoveAnimation(CenterPointProperty); // remove holding animation in WPF
UpdateTransform(); UpdateTransform();
} }
@ -650,8 +531,10 @@ namespace MapControl
{ {
if (!internalPropertyChange) if (!internalPropertyChange)
{ {
centerPoint.X = Location.NormalizeLongitude(centerPoint.X); var center = MapProjection.PointToLocation(centerPoint);
InternalSetValue(CenterProperty, mapTransform.Transform(centerPoint)); center.Longitude = Location.NormalizeLongitude(center.Longitude);
InternalSetValue(CenterProperty, center);
UpdateTransform(); UpdateTransform();
} }
} }
@ -742,6 +625,7 @@ namespace MapControl
InternalSetValue(ZoomLevelProperty, TargetZoomLevel); InternalSetValue(ZoomLevelProperty, TargetZoomLevel);
RemoveAnimation(ZoomLevelProperty); // remove holding animation in WPF RemoveAnimation(ZoomLevelProperty); // remove holding animation in WPF
UpdateTransform(true); UpdateTransform(true);
} }
} }
@ -819,22 +703,29 @@ namespace MapControl
} }
} }
private void UpdateTransform(bool resetOrigin = false) private void InternalSetValue(DependencyProperty property, object value)
{ {
var mapOriginX = MapOrigin.X; internalPropertyChange = true;
var center = transformOrigin ?? Center; SetValue(property, value);
internalPropertyChange = false;
}
SetViewportTransform(center); private void UpdateTransform(bool resetTransformCenter = false, bool projectionChanged = false)
{
var projection = MapProjection;
var center = transformCenter ?? Center;
if (transformOrigin != null) projection.SetViewportTransform(ProjectionCenter ?? Center, center, viewportCenter, ZoomLevel, Heading);
if (transformCenter != null)
{ {
center = ViewportPointToLocation(new Point(RenderSize.Width / 2d, RenderSize.Height / 2d)); center = projection.ViewportPointToLocation(new Point(RenderSize.Width / 2d, RenderSize.Height / 2d));
center.Longitude = Location.NormalizeLongitude(center.Longitude); center.Longitude = Location.NormalizeLongitude(center.Longitude);
if (center.Latitude < -mapTransform.MaxLatitude || center.Latitude > mapTransform.MaxLatitude) if (center.Latitude < -projection.MaxLatitude || center.Latitude > projection.MaxLatitude)
{ {
center.Latitude = Math.Min(Math.Max(center.Latitude, -mapTransform.MaxLatitude), mapTransform.MaxLatitude); center.Latitude = Math.Min(Math.Max(center.Latitude, -projection.MaxLatitude), projection.MaxLatitude);
resetOrigin = true; resetTransformCenter = true;
} }
InternalSetValue(CenterProperty, center); InternalSetValue(CenterProperty, center);
@ -842,22 +733,24 @@ namespace MapControl
if (centerAnimation == null) if (centerAnimation == null)
{ {
InternalSetValue(TargetCenterProperty, center); InternalSetValue(TargetCenterProperty, center);
InternalSetValue(CenterPointProperty, mapTransform.Transform(center)); InternalSetValue(CenterPointProperty, projection.LocationToPoint(center));
} }
if (resetOrigin) if (resetTransformCenter)
{ {
ResetTransformOrigin(); ResetTransformCenter();
SetViewportTransform(center); projection.SetViewportTransform(ProjectionCenter ?? center, center, viewportCenter, ZoomLevel, Heading);
} }
} }
CenterScale = ViewportScale * mapTransform.RelativeScale(center) / TileSource.MetersPerDegree; var scale = projection.GetMapScale(center);
scaleTransform.ScaleX = CenterScale; ScaleTransform.ScaleX = scale.X;
scaleTransform.ScaleY = CenterScale; ScaleTransform.ScaleY = scale.Y;
rotateTransform.Angle = Heading; RotateTransform.Angle = Heading;
OnViewportChanged(new ViewportChangedEventArgs(MapOrigin.X - mapOriginX)); OnViewportChanged(new ViewportChangedEventArgs(projectionChanged, Center.Longitude - centerLongitude));
centerLongitude = Center.Longitude;
} }
protected override void OnViewportChanged(ViewportChangedEventArgs e) protected override void OnViewportChanged(ViewportChangedEventArgs e)

View file

@ -68,13 +68,18 @@
<Reference Include="System.Xml" /> <Reference Include="System.Xml" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="AzimuthalEquidistantProjection.cs" />
<Compile Include="AzimuthalProjection.cs" />
<Compile Include="BingMapsTileLayer.cs" /> <Compile Include="BingMapsTileLayer.cs" />
<Compile Include="BingMapsTileSource.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="Extensions.Silverlight.cs" />
<Compile Include="GnomonicProjection.cs" />
<Compile Include="HyperlinkText.cs" /> <Compile Include="HyperlinkText.cs" />
<Compile Include="ImageTileSource.Silverlight.WinRT.cs" /> <Compile Include="ImageTileSource.Silverlight.WinRT.cs" />
<Compile Include="IMapElement.cs" />
<Compile Include="ITileImageLoader.cs" />
<Compile Include="Location.cs" /> <Compile Include="Location.cs" />
<Compile Include="LocationCollection.cs" /> <Compile Include="LocationCollection.cs" />
<Compile Include="LocationCollectionConverter.cs" /> <Compile Include="LocationCollectionConverter.cs" />
@ -82,11 +87,10 @@
<Compile Include="Map.Silverlight.cs" /> <Compile Include="Map.Silverlight.cs" />
<Compile Include="MapBase.cs" /> <Compile Include="MapBase.cs" />
<Compile Include="MapBase.Silverlight.WinRT.cs" /> <Compile Include="MapBase.Silverlight.WinRT.cs" />
<Compile Include="MapGraticule.cs" />
<Compile Include="MapGraticule.Silverlight.WinRT.cs" />
<Compile Include="MapImage.cs" />
<Compile Include="MapImageLayer.cs" /> <Compile Include="MapImageLayer.cs" />
<Compile Include="MapImageLayer.Silverlight.WinRT.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="MapItem.Silverlight.WinRT.cs" />
<Compile Include="MapItemsControl.Silverlight.WinRT.cs" /> <Compile Include="MapItemsControl.Silverlight.WinRT.cs" />
<Compile Include="MapOverlay.cs" /> <Compile Include="MapOverlay.cs" />
@ -97,23 +101,23 @@
<Compile Include="MapPath.Silverlight.WinRT.cs" /> <Compile Include="MapPath.Silverlight.WinRT.cs" />
<Compile Include="MapPolyline.cs" /> <Compile Include="MapPolyline.cs" />
<Compile Include="MapPolyline.Silverlight.WinRT.cs" /> <Compile Include="MapPolyline.Silverlight.WinRT.cs" />
<Compile Include="MapRectangle.cs" /> <Compile Include="MapProjection.cs" />
<Compile Include="MapTransform.cs" /> <Compile Include="MapTileLayer.cs" />
<Compile Include="MatrixEx.cs" /> <Compile Include="MatrixEx.Silverlight.WinRT.cs" />
<Compile Include="MercatorTransform.cs" /> <Compile Include="OrthographicProjection.cs" />
<Compile Include="PanelBase.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Pushpin.Silverlight.WinRT.cs" /> <Compile Include="Pushpin.Silverlight.WinRT.cs" />
<Compile Include="StereographicProjection.cs" />
<Compile Include="Tile.cs" /> <Compile Include="Tile.cs" />
<Compile Include="Tile.Silverlight.WinRT.cs" /> <Compile Include="Tile.Silverlight.WinRT.cs" />
<Compile Include="TileGrid.cs" /> <Compile Include="TileGrid.cs" />
<Compile Include="TileImageLoader.Silverlight.cs" /> <Compile Include="TileImageLoader.Silverlight.cs" />
<Compile Include="TileLayer.cs" /> <Compile Include="MapTileLayer.Silverlight.WinRT.cs" />
<Compile Include="TileLayer.Silverlight.WinRT.cs" />
<Compile Include="TileLayerCollection.cs" />
<Compile Include="TileSource.cs" /> <Compile Include="TileSource.cs" />
<Compile Include="TileSourceConverter.cs" /> <Compile Include="TileSourceConverter.cs" />
<Compile Include="ViewportChangedEventArgs.cs" /> <Compile Include="ViewportChangedEventArgs.cs" />
<Compile Include="WebMercatorProjection.cs" />
<Compile Include="WmsImageLayer.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Page Include="Themes\Generic.xaml"> <Page Include="Themes\Generic.xaml">

View file

@ -52,13 +52,18 @@
<Reference Include="WindowsBase" /> <Reference Include="WindowsBase" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="AzimuthalEquidistantProjection.cs" />
<Compile Include="AzimuthalProjection.cs" />
<Compile Include="BingMapsTileLayer.cs" /> <Compile Include="BingMapsTileLayer.cs" />
<Compile Include="BingMapsTileSource.cs" /> <Compile Include="BingMapsTileSource.cs" />
<Compile Include="ImageLoader.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="HyperlinkText.cs" />
<Compile Include="ImageTileSource.WPF.cs" /> <Compile Include="ImageTileSource.WPF.cs" />
<Compile Include="IMapElement.cs" />
<Compile Include="ITileImageLoader.cs" />
<Compile Include="Location.cs" /> <Compile Include="Location.cs" />
<Compile Include="LocationCollection.cs" /> <Compile Include="LocationCollection.cs" />
<Compile Include="LocationCollectionConverter.cs" /> <Compile Include="LocationCollectionConverter.cs" />
@ -66,41 +71,39 @@
<Compile Include="Map.WPF.cs" /> <Compile Include="Map.WPF.cs" />
<Compile Include="MapBase.cs" /> <Compile Include="MapBase.cs" />
<Compile Include="MapBase.WPF.cs" /> <Compile Include="MapBase.WPF.cs" />
<Compile Include="MapGraticule.cs" />
<Compile Include="MapGraticule.WPF.cs" />
<Compile Include="MapImage.cs" />
<Compile Include="MapImageLayer.cs" /> <Compile Include="MapImageLayer.cs" />
<Compile Include="MapImageLayer.WPF.cs" /> <Compile Include="MapImageLayer.WPF.cs" />
<Compile Include="MapGraticule.cs" />
<Compile Include="MapGraticule.WPF.cs" />
<Compile Include="MapItem.WPF.cs" /> <Compile Include="MapItem.WPF.cs" />
<Compile Include="MapItemsControl.WPF.cs" /> <Compile Include="MapItemsControl.WPF.cs" />
<Compile Include="MapOverlay.cs" /> <Compile Include="MapOverlay.cs" />
<Compile Include="MapOverlay.WPF.cs" /> <Compile Include="MapOverlay.WPF.cs" />
<Compile Include="MapPanel.cs" /> <Compile Include="MapPanel.cs" />
<Compile Include="MapPanel.WPF.cs" /> <Compile Include="MapPanel.WPF.cs" />
<Compile Include="MapRectangle.WPF.cs" />
<Compile Include="MapPath.cs" /> <Compile Include="MapPath.cs" />
<Compile Include="MapPath.WPF.cs" /> <Compile Include="MapPath.WPF.cs" />
<Compile Include="MapPolyline.cs" /> <Compile Include="MapPolyline.cs" />
<Compile Include="MapPolyline.WPF.cs" /> <Compile Include="MapPolyline.WPF.cs" />
<Compile Include="MapRectangle.cs" />
<Compile Include="MapScale.cs" /> <Compile Include="MapScale.cs" />
<Compile Include="MapTransform.cs" /> <Compile Include="MapProjection.cs" />
<Compile Include="MercatorTransform.cs" /> <Compile Include="MatrixEx.WPF.cs" />
<Compile Include="PanelBase.cs" /> <Compile Include="OrthographicProjection.cs" />
<Compile Include="StereographicProjection.cs" />
<Compile Include="WebMercatorProjection.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Pushpin.WPF.cs" /> <Compile Include="Pushpin.WPF.cs" />
<Compile Include="Tile.cs" /> <Compile Include="Tile.cs" />
<Compile Include="Tile.WPF.cs" /> <Compile Include="Tile.WPF.cs" />
<Compile Include="TileGrid.cs" /> <Compile Include="TileGrid.cs" />
<Compile Include="TileImageLoader.WPF.cs" /> <Compile Include="TileImageLoader.WPF.cs" />
<Compile Include="TileLayer.cs" /> <Compile Include="MapTileLayer.cs" />
<Compile Include="TileLayer.WPF.cs" /> <Compile Include="MapTileLayer.WPF.cs" />
<Compile Include="TileLayerCollection.cs" />
<Compile Include="TileSource.cs" /> <Compile Include="TileSource.cs" />
<Compile Include="TileSourceConverter.cs" /> <Compile Include="TileSourceConverter.cs" />
<Compile Include="ViewportChangedEventArgs.cs" /> <Compile Include="ViewportChangedEventArgs.cs" />
<Compile Include="WmsImageLayer.cs" /> <Compile Include="WmsImageLayer.cs" />
<Compile Include="WmsImageLayer.WPF.cs" /> <Compile Include="WmsLayers.WPF.WinRT.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Page Include="Themes\Generic.xaml"> <Page Include="Themes\Generic.xaml">

View file

@ -1,5 +1,5 @@
// XAML Map Control - http://xamlmapcontrol.codeplex.com/ // XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2016 Clemens Fischer // © 2017 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL) // Licensed under the Microsoft Public License (Ms-PL)
using System; using System;
@ -34,169 +34,180 @@ namespace MapControl
protected override void OnViewportChanged(ViewportChangedEventArgs e) protected override void OnViewportChanged(ViewportChangedEventArgs e)
{ {
if (path == null) var projection = ParentMap.MapProjection;
if (!double.IsNaN(projection.LongitudeScale))
{ {
path = new Path if (path == null)
{ {
Data = new PathGeometry() path = new Path
};
path.SetBinding(Shape.StrokeProperty,
GetBindingExpression(StrokeProperty)?.ParentBinding ??
new Binding
{ {
Source = this, Data = new PathGeometry()
Path = new PropertyPath("Stroke")
});
path.SetBinding(Shape.StrokeThicknessProperty,
GetBindingExpression(StrokeThicknessProperty)?.ParentBinding ??
new Binding
{
Source = this,
Path = new PropertyPath("StrokeThickness")
});
Children.Add(path);
}
var bounds = ParentMap.ViewportTransform.Inverse.TransformBounds(new Rect(new Point(), ParentMap.RenderSize));
var start = ParentMap.MapTransform.Transform(new Point(bounds.X, bounds.Y));
var end = ParentMap.MapTransform.Transform(new Point(bounds.X + bounds.Width, bounds.Y + bounds.Height));
var lineDistance = GetLineDistance();
var labelStart = new Location(
Math.Ceiling(start.Latitude / lineDistance) * lineDistance,
Math.Ceiling(start.Longitude / lineDistance) * lineDistance);
var labelEnd = new Location(
Math.Floor(end.Latitude / lineDistance) * lineDistance,
Math.Floor(end.Longitude / lineDistance) * lineDistance);
var lineStart = new Location(
Math.Min(Math.Max(labelStart.Latitude - lineDistance, -ParentMap.MapTransform.MaxLatitude), ParentMap.MapTransform.MaxLatitude),
labelStart.Longitude - lineDistance);
var lineEnd = new Location(
Math.Min(Math.Max(labelEnd.Latitude + lineDistance, -ParentMap.MapTransform.MaxLatitude), ParentMap.MapTransform.MaxLatitude),
labelEnd.Longitude + lineDistance);
if (!lineStart.Equals(graticuleStart) || !lineEnd.Equals(graticuleEnd))
{
graticuleStart = lineStart;
graticuleEnd = lineEnd;
var geometry = (PathGeometry)path.Data;
geometry.Figures.Clear();
geometry.Transform = ParentMap.ViewportTransform;
for (var lat = labelStart.Latitude; lat <= end.Latitude; lat += lineDistance)
{
var figure = new PathFigure
{
StartPoint = ParentMap.MapTransform.Transform(new Location(lat, lineStart.Longitude)),
IsClosed = false,
IsFilled = false
}; };
figure.Segments.Add(new LineSegment path.SetBinding(Shape.StrokeProperty,
{ GetBindingExpression(StrokeProperty)?.ParentBinding ??
Point = ParentMap.MapTransform.Transform(new Location(lat, lineEnd.Longitude)), new Binding
}); {
Source = this,
Path = new PropertyPath("Stroke")
});
geometry.Figures.Add(figure); path.SetBinding(Shape.StrokeThicknessProperty,
GetBindingExpression(StrokeThicknessProperty)?.ParentBinding ??
new Binding
{
Source = this,
Path = new PropertyPath("StrokeThickness")
});
Children.Add(path);
} }
for (var lon = labelStart.Longitude; lon <= end.Longitude; lon += lineDistance) var bounds = projection.ViewportRectToBoundingBox(new Rect(0d, 0d, ParentMap.RenderSize.Width, ParentMap.RenderSize.Height));
var lineDistance = GetLineDistance();
var labelStart = new Location(
Math.Ceiling(bounds.South / lineDistance) * lineDistance,
Math.Ceiling(bounds.West / lineDistance) * lineDistance);
var labelEnd = new Location(
Math.Floor(bounds.North / lineDistance) * lineDistance,
Math.Floor(bounds.East / lineDistance) * lineDistance);
var lineStart = new Location(
Math.Min(Math.Max(labelStart.Latitude - lineDistance, -projection.MaxLatitude), projection.MaxLatitude),
labelStart.Longitude - lineDistance);
var lineEnd = new Location(
Math.Min(Math.Max(labelEnd.Latitude + lineDistance, -projection.MaxLatitude), projection.MaxLatitude),
labelEnd.Longitude + lineDistance);
if (!lineStart.Equals(graticuleStart) || !lineEnd.Equals(graticuleEnd))
{ {
var figure = new PathFigure graticuleStart = lineStart;
graticuleEnd = lineEnd;
var geometry = (PathGeometry)path.Data;
geometry.Figures.Clear();
geometry.Transform = projection.ViewportTransform;
for (var lat = labelStart.Latitude; lat <= bounds.North; lat += lineDistance)
{ {
StartPoint = ParentMap.MapTransform.Transform(new Location(lineStart.Latitude, lon)), var figure = new PathFigure
IsClosed = false,
IsFilled = false
};
figure.Segments.Add(new LineSegment
{
Point = ParentMap.MapTransform.Transform(new Location(lineEnd.Latitude, lon)),
});
geometry.Figures.Add(figure);
}
var labelFormat = GetLabelFormat(lineDistance);
var childIndex = 1; // 0 for Path
for (var lat = labelStart.Latitude; lat <= end.Latitude; lat += lineDistance)
{
for (var lon = labelStart.Longitude; lon <= end.Longitude; lon += lineDistance)
{
TextBlock label;
if (childIndex < Children.Count)
{ {
label = (TextBlock)Children[childIndex]; StartPoint = projection.LocationToPoint(new Location(lat, lineStart.Longitude)),
} IsClosed = false,
else IsFilled = false
{ };
var renderTransform = new TransformGroup();
renderTransform.Children.Add(new TranslateTransform());
renderTransform.Children.Add(ParentMap.RotateTransform);
renderTransform.Children.Add(new TranslateTransform());
label = new TextBlock figure.Segments.Add(new LineSegment
{
Point = projection.LocationToPoint(new Location(lat, lineEnd.Longitude)),
});
geometry.Figures.Add(figure);
}
for (var lon = labelStart.Longitude; lon <= bounds.East; lon += lineDistance)
{
var figure = new PathFigure
{
StartPoint = projection.LocationToPoint(new Location(lineStart.Latitude, lon)),
IsClosed = false,
IsFilled = false
};
figure.Segments.Add(new LineSegment
{
Point = projection.LocationToPoint(new Location(lineEnd.Latitude, lon)),
});
geometry.Figures.Add(figure);
}
var labelFormat = GetLabelFormat(lineDistance);
var childIndex = 1; // 0 for Path
for (var lat = labelStart.Latitude; lat <= bounds.North; lat += lineDistance)
{
for (var lon = labelStart.Longitude; lon <= bounds.East; lon += lineDistance)
{
TextBlock label;
if (childIndex < Children.Count)
{ {
RenderTransform = renderTransform label = (TextBlock)Children[childIndex];
}; }
else
{
var renderTransform = new TransformGroup();
renderTransform.Children.Add(new TranslateTransform());
renderTransform.Children.Add(ParentMap.RotateTransform);
renderTransform.Children.Add(new TranslateTransform());
label.SetBinding(TextBlock.ForegroundProperty, label = new TextBlock
GetBindingExpression(ForegroundProperty)?.ParentBinding ??
new Binding
{ {
Source = this, RenderTransform = renderTransform
Path = new PropertyPath("Foreground") };
});
Children.Add(label); label.SetBinding(TextBlock.ForegroundProperty,
GetBindingExpression(ForegroundProperty)?.ParentBinding ??
new Binding
{
Source = this,
Path = new PropertyPath("Foreground")
});
Children.Add(label);
}
childIndex++;
if (FontFamily != null)
{
label.FontFamily = FontFamily;
}
label.FontSize = FontSize;
label.FontStyle = FontStyle;
label.FontStretch = FontStretch;
label.FontWeight = FontWeight;
label.Text = GetLabelText(lat, labelFormat, "NS") + "\n" + GetLabelText(Location.NormalizeLongitude(lon), labelFormat, "EW");
label.Tag = new Location(lat, lon);
label.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
var translateTransform = (TranslateTransform)((TransformGroup)label.RenderTransform).Children[0];
translateTransform.X = StrokeThickness / 2d + 2d;
translateTransform.Y = -label.DesiredSize.Height / 2d;
} }
}
childIndex++; while (Children.Count > childIndex)
{
if (FontFamily != null) Children.RemoveAt(Children.Count - 1);
{
label.FontFamily = FontFamily;
}
label.FontSize = FontSize;
label.FontStyle = FontStyle;
label.FontStretch = FontStretch;
label.FontWeight = FontWeight;
label.Text = GetLabelText(lat, labelFormat, "NS") + "\n" + GetLabelText(Location.NormalizeLongitude(lon), labelFormat, "EW");
label.Tag = new Location(lat, lon);
label.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
var translateTransform = (TranslateTransform)((TransformGroup)label.RenderTransform).Children[0];
translateTransform.X = StrokeThickness / 2d + 2d;
translateTransform.Y = -label.DesiredSize.Height / 2d;
} }
} }
while (Children.Count > childIndex) // don't use MapPanel.Location because labels may be at more than 180° distance from map center
for (int i = 1; i < Children.Count; i++)
{ {
Children.RemoveAt(Children.Count - 1); var label = (TextBlock)Children[i];
var location = (Location)label.Tag;
var viewportTransform = (TranslateTransform)((TransformGroup)label.RenderTransform).Children[2];
var viewportPosition = projection.LocationToViewportPoint(location);
viewportTransform.X = viewportPosition.X;
viewportTransform.Y = viewportPosition.Y;
} }
} }
else if (path != null)
// don't use MapPanel.Location because labels may be at more than 180° distance from map center
for (int i = 1; i < Children.Count; i++)
{ {
var label = (TextBlock)Children[i]; path = null;
var location = (Location)label.Tag; graticuleStart = null;
var viewportTransform = (TranslateTransform)((TransformGroup)label.RenderTransform).Children[2]; graticuleEnd = null;
var viewportPosition = ParentMap.LocationToViewportPoint(location);
viewportTransform.X = viewportPosition.X; Children.Clear();
viewportTransform.Y = viewportPosition.Y;
} }
base.OnViewportChanged(e); base.OnViewportChanged(e);

View file

@ -1,5 +1,5 @@
// XAML Map Control - http://xamlmapcontrol.codeplex.com/ // XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2016 Clemens Fischer // © 2017 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL) // Licensed under the Microsoft Public License (Ms-PL)
using System; using System;
@ -26,11 +26,8 @@ namespace MapControl
static MapGraticule() static MapGraticule()
{ {
IsHitTestVisibleProperty.OverrideMetadata( IsHitTestVisibleProperty.OverrideMetadata(typeof(MapGraticule), new FrameworkPropertyMetadata(false));
typeof(MapGraticule), new FrameworkPropertyMetadata(false)); StrokeThicknessProperty.OverrideMetadata(typeof(MapGraticule), new FrameworkPropertyMetadata(0.5));
StrokeThicknessProperty.OverrideMetadata(
typeof(MapGraticule), new FrameworkPropertyMetadata(0.5));
} }
protected override void OnViewportChanged(ViewportChangedEventArgs e) protected override void OnViewportChanged(ViewportChangedEventArgs e)
@ -40,45 +37,45 @@ namespace MapControl
protected override void OnRender(DrawingContext drawingContext) protected override void OnRender(DrawingContext drawingContext)
{ {
if (ParentMap != null) var projection = ParentMap?.MapProjection;
if (projection != null && !double.IsNaN(projection.LongitudeScale))
{ {
var bounds = ParentMap.ViewportTransform.Inverse.TransformBounds(new Rect(ParentMap.RenderSize)); var bounds = projection.ViewportRectToBoundingBox(new Rect(ParentMap.RenderSize));
var start = ParentMap.MapTransform.Transform(new Point(bounds.X, bounds.Y));
var end = ParentMap.MapTransform.Transform(new Point(bounds.X + bounds.Width, bounds.Y + bounds.Height));
var lineDistance = GetLineDistance(); var lineDistance = GetLineDistance();
var labelFormat = GetLabelFormat(lineDistance); var labelFormat = GetLabelFormat(lineDistance);
var latLabelStart = Math.Ceiling(start.Latitude / lineDistance) * lineDistance; var latLabelStart = Math.Ceiling(bounds.South / lineDistance) * lineDistance;
var lonLabelStart = Math.Ceiling(start.Longitude / lineDistance) * lineDistance; var lonLabelStart = Math.Ceiling(bounds.West / lineDistance) * lineDistance;
var latLabels = new List<Label>((int)((end.Latitude - latLabelStart) / lineDistance) + 1); var latLabels = new List<Label>((int)((bounds.North - latLabelStart) / lineDistance) + 1);
var lonLabels = new List<Label>((int)((end.Longitude - lonLabelStart) / lineDistance) + 1); var lonLabels = new List<Label>((int)((bounds.East - lonLabelStart) / lineDistance) + 1);
for (var lat = latLabelStart; lat <= end.Latitude; lat += lineDistance) for (var lat = latLabelStart; lat <= bounds.North; lat += lineDistance)
{ {
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)));
drawingContext.DrawLine(Pen, drawingContext.DrawLine(Pen,
ParentMap.LocationToViewportPoint(new Location(lat, start.Longitude)), projection.LocationToViewportPoint(new Location(lat, bounds.West)),
ParentMap.LocationToViewportPoint(new Location(lat, end.Longitude))); projection.LocationToViewportPoint(new Location(lat, bounds.East)));
} }
for (var lon = lonLabelStart; lon <= end.Longitude; lon += lineDistance) for (var lon = lonLabelStart; lon <= bounds.East; lon += lineDistance)
{ {
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)));
drawingContext.DrawLine(Pen, drawingContext.DrawLine(Pen,
ParentMap.LocationToViewportPoint(new Location(start.Latitude, lon)), projection.LocationToViewportPoint(new Location(bounds.South, lon)),
ParentMap.LocationToViewportPoint(new Location(end.Latitude, lon))); projection.LocationToViewportPoint(new Location(bounds.North, lon)));
} }
foreach (var latLabel in latLabels) foreach (var latLabel in latLabels)
{ {
foreach (var lonLabel in lonLabels) foreach (var lonLabel in lonLabels)
{ {
var position = ParentMap.LocationToViewportPoint(new Location(latLabel.Position, lonLabel.Position)); var position = projection.LocationToViewportPoint(new Location(latLabel.Position, lonLabel.Position));
drawingContext.PushTransform(new RotateTransform(ParentMap.Heading, position.X, position.Y)); drawingContext.PushTransform(new RotateTransform(ParentMap.Heading, position.X, position.Y));
drawingContext.DrawText(latLabel.Text, drawingContext.DrawText(latLabel.Text,

View file

@ -1,5 +1,5 @@
// XAML Map Control - http://xamlmapcontrol.codeplex.com/ // XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2016 Clemens Fischer // © 2017 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL) // Licensed under the Microsoft Public License (Ms-PL)
using System; using System;
@ -17,7 +17,7 @@ namespace MapControl
public partial class MapGraticule : MapOverlay public partial class MapGraticule : MapOverlay
{ {
public static readonly DependencyProperty MinLineDistanceProperty = DependencyProperty.Register( public static readonly DependencyProperty MinLineDistanceProperty = DependencyProperty.Register(
"MinLineDistance", typeof(double), typeof(MapGraticule), new PropertyMetadata(150d)); nameof(MinLineDistance), typeof(double), typeof(MapGraticule), new PropertyMetadata(150d));
/// <summary> /// <summary>
/// Minimum graticule line distance in pixels. The default value is 150. /// Minimum graticule line distance in pixels. The default value is 150.

View file

@ -1,41 +0,0 @@
// XAML Map Control - http://xamlmapcontrol.codeplex.com/
// © 2016 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
{
/// <summary>
/// Fills a rectangular area with an ImageBrush from the Source property.
/// </summary>
public class MapImage : MapRectangle
{
public static readonly DependencyProperty SourceProperty = DependencyProperty.Register(
"Source", typeof(ImageSource), typeof(MapImage),
new PropertyMetadata(null, (o, e) => ((ImageBrush)((MapImage)o).Fill).ImageSource = (ImageSource)e.NewValue));
public MapImage()
{
Fill = new ImageBrush
{
RelativeTransform = new MatrixTransform
{
Matrix = new Matrix(1d, 0d, 0d, -1d, 0d, 1d)
}
};
}
public ImageSource Source
{
get { return (ImageSource)GetValue(SourceProperty); }
set { SetValue(SourceProperty, value); }
}
}
}

View file

@ -1,13 +1,15 @@
// XAML Map Control - http://xamlmapcontrol.codeplex.com/ // XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2016 Clemens Fischer // © 2017 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL) // Licensed under the Microsoft Public License (Ms-PL)
using System; using System;
#if NETFX_CORE #if NETFX_CORE
using Windows.UI.Xaml; using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media.Imaging; using Windows.UI.Xaml.Media.Imaging;
#else #else
using System.Windows; using System.Windows;
using System.Windows.Controls;
using System.Windows.Media.Imaging; using System.Windows.Media.Imaging;
#endif #endif
@ -15,7 +17,7 @@ namespace MapControl
{ {
public partial class MapImageLayer public partial class MapImageLayer
{ {
protected virtual void UpdateImage(Uri uri) protected void UpdateImage(Uri uri)
{ {
UpdateImage(new BitmapImage(uri)); UpdateImage(new BitmapImage(uri));
} }
@ -52,9 +54,7 @@ namespace MapControl
bitmap.ImageOpened -= BitmapImageOpened; bitmap.ImageOpened -= BitmapImageOpened;
bitmap.ImageFailed -= BitmapImageFailed; bitmap.ImageFailed -= BitmapImageFailed;
var mapImage = (MapImage)Children[currentImageIndex]; ((Image)Children[topImageIndex]).Source = null;
mapImage.Source = null;
SwapImages(); SwapImages();
} }
} }

View file

@ -1,9 +1,10 @@
// XAML Map Control - http://xamlmapcontrol.codeplex.com/ // XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2016 Clemens Fischer // © 2017 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL) // Licensed under the Microsoft Public License (Ms-PL)
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows.Controls;
using System.Windows.Media; using System.Windows.Media;
using System.Windows.Media.Imaging; using System.Windows.Media.Imaging;
@ -11,11 +12,11 @@ namespace MapControl
{ {
public partial class MapImageLayer public partial class MapImageLayer
{ {
protected virtual void UpdateImage(Uri uri) protected void UpdateImage(Uri uri)
{ {
Task.Run(() => Task.Run(() =>
{ {
var image = ImageLoader.FromUri(uri); var image = BitmapSourceHelper.FromUri(uri);
Dispatcher.BeginInvoke(new Action(() => UpdateImage(image))); Dispatcher.BeginInvoke(new Action(() => UpdateImage(image)));
}); });
} }
@ -50,9 +51,7 @@ namespace MapControl
bitmap.DownloadCompleted -= BitmapDownloadCompleted; bitmap.DownloadCompleted -= BitmapDownloadCompleted;
bitmap.DownloadFailed -= BitmapDownloadFailed; bitmap.DownloadFailed -= BitmapDownloadFailed;
var mapImage = (MapImage)Children[currentImageIndex]; ((Image)Children[topImageIndex]).Source = null;
mapImage.Source = null;
SwapImages(); SwapImages();
} }
} }

View file

@ -1,19 +1,20 @@
// XAML Map Control - http://xamlmapcontrol.codeplex.com/ // XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2016 Clemens Fischer // © 2017 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL) // Licensed under the Microsoft Public License (Ms-PL)
using System; using System;
using System.Globalization; using System.Diagnostics;
using System.Linq;
#if NETFX_CORE #if NETFX_CORE
using Windows.Foundation; using Windows.Foundation;
using Windows.UI.Xaml; using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media.Imaging; using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Media.Animation; using Windows.UI.Xaml.Media.Animation;
using Windows.UI.Xaml.Media.Imaging;
#else #else
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Animation; using System.Windows.Media.Animation;
using System.Windows.Media.Imaging; using System.Windows.Media.Imaging;
using System.Windows.Threading; using System.Windows.Threading;
@ -22,96 +23,59 @@ using System.Windows.Threading;
namespace MapControl namespace MapControl
{ {
/// <summary> /// <summary>
/// Map image overlay. Fills the entire viewport with a map image provided by a web service, /// Map image layer. Fills the entire viewport with a map image, e.g. provided by a Web Map Service (WMS).
/// e.g. a Web Map Service (WMS). The image request Uri is specified by the UriFormat property. /// The image must be provided by the abstract UpdateImage(BoundingBox) method.
/// </summary> /// </summary>
public partial class MapImageLayer : MapPanel public abstract partial class MapImageLayer : MapPanel, IMapLayer
{ {
public struct BoundingBox
{
public readonly double West;
public readonly double East;
public readonly double South;
public readonly double North;
public BoundingBox(double west, double east, double south, double north)
{
West = west;
East = east;
South = south;
North = north;
}
}
public static readonly DependencyProperty UriFormatProperty = DependencyProperty.Register(
"UriFormat", typeof(string), typeof(MapImageLayer),
new PropertyMetadata(null, (o, e) => ((MapImageLayer)o).UpdateImage()));
public static readonly DependencyProperty MinLongitudeProperty = DependencyProperty.Register(
"MinLongitude", typeof(double), typeof(MapImageLayer), new PropertyMetadata(double.NaN));
public static readonly DependencyProperty MaxLongitudeProperty = DependencyProperty.Register(
"MaxLongitude", typeof(double), typeof(MapImageLayer), new PropertyMetadata(double.NaN));
public static readonly DependencyProperty MinLatitudeProperty = DependencyProperty.Register( public static readonly DependencyProperty MinLatitudeProperty = DependencyProperty.Register(
"MinLatitude", typeof(double), typeof(MapImageLayer), new PropertyMetadata(double.NaN)); nameof(MinLatitude), typeof(double), typeof(MapImageLayer), new PropertyMetadata(double.NaN));
public static readonly DependencyProperty MaxLatitudeProperty = DependencyProperty.Register( public static readonly DependencyProperty MaxLatitudeProperty = DependencyProperty.Register(
"MaxLatitude", typeof(double), typeof(MapImageLayer), new PropertyMetadata(double.NaN)); nameof(MaxLatitude), typeof(double), typeof(MapImageLayer), new PropertyMetadata(double.NaN));
public static readonly DependencyProperty MinLongitudeProperty = DependencyProperty.Register(
nameof(MinLongitude), typeof(double), typeof(MapImageLayer), new PropertyMetadata(double.NaN));
public static readonly DependencyProperty MaxLongitudeProperty = DependencyProperty.Register(
nameof(MaxLongitude), typeof(double), typeof(MapImageLayer), new PropertyMetadata(double.NaN));
public static readonly DependencyProperty MaxBoundingBoxWidthProperty = DependencyProperty.Register( public static readonly DependencyProperty MaxBoundingBoxWidthProperty = DependencyProperty.Register(
"MaxBoundingBoxWidth", typeof(double), typeof(MapImageLayer), new PropertyMetadata(double.NaN)); nameof(MaxBoundingBoxWidth), typeof(double), typeof(MapImageLayer), new PropertyMetadata(double.NaN));
public static readonly DependencyProperty RelativeImageSizeProperty = DependencyProperty.Register( public static readonly DependencyProperty RelativeImageSizeProperty = DependencyProperty.Register(
"RelativeImageSize", typeof(double), typeof(MapImageLayer), new PropertyMetadata(1d)); nameof(RelativeImageSize), typeof(double), typeof(MapImageLayer), new PropertyMetadata(1d));
public static readonly DependencyProperty UpdateIntervalProperty = DependencyProperty.Register( public static readonly DependencyProperty UpdateIntervalProperty = DependencyProperty.Register(
"UpdateInterval", typeof(TimeSpan), typeof(MapImageLayer), nameof(UpdateInterval), typeof(TimeSpan), typeof(MapImageLayer),
new PropertyMetadata(TimeSpan.FromSeconds(0.5), (o, e) => ((MapImageLayer)o).updateTimer.Interval = (TimeSpan)e.NewValue)); new PropertyMetadata(TimeSpan.FromSeconds(0.2), (o, e) => ((MapImageLayer)o).updateTimer.Interval = (TimeSpan)e.NewValue));
public static readonly DependencyProperty UpdateWhileViewportChangingProperty = DependencyProperty.Register(
nameof(UpdateWhileViewportChanging), typeof(bool), typeof(MapImageLayer), new PropertyMetadata(false));
public static readonly DependencyProperty DescriptionProperty = DependencyProperty.Register(
nameof(Description), typeof(string), typeof(MapImageLayer), new PropertyMetadata(null));
public static readonly DependencyProperty MapBackgroundProperty = DependencyProperty.Register(
nameof(MapBackground), typeof(Brush), typeof(MapImageLayer), new PropertyMetadata(null));
public static readonly DependencyProperty MapForegroundProperty = DependencyProperty.Register(
nameof(MapForeground), typeof(Brush), typeof(MapImageLayer), new PropertyMetadata(null));
private readonly DispatcherTimer updateTimer; private readonly DispatcherTimer updateTimer;
private int currentImageIndex; private BoundingBox boundingBox;
private int topImageIndex;
private bool updateInProgress; private bool updateInProgress;
public MapImageLayer() public MapImageLayer()
{ {
Children.Add(new MapImage { Opacity = 0d }); Children.Add(new Image { Opacity = 0d, Stretch = Stretch.Fill });
Children.Add(new MapImage { Opacity = 0d }); Children.Add(new Image { Opacity = 0d, Stretch = Stretch.Fill });
updateTimer = new DispatcherTimer { Interval = UpdateInterval }; updateTimer = new DispatcherTimer { Interval = UpdateInterval };
updateTimer.Tick += (s, e) => UpdateImage(); updateTimer.Tick += (s, e) => UpdateImage();
} }
/// <summary>
/// The format string of the image request Uri. The format must contain
/// {X} and {Y} format specifiers for the map width and height in pixels and either
/// {w},{s},{e},{n} for a latitude/longitude bounding box (like EPSG:4326) or
/// {W},{S},{E},{N} for a projected bounding box (e.g. in meters like EPSG:3857).
/// </summary>
public string UriFormat
{
get { return (string)GetValue(UriFormatProperty); }
set { SetValue(UriFormatProperty, value); }
}
/// <summary>
/// Optional minimum longitude value. Default is NaN.
/// </summary>
public double MinLongitude
{
get { return (double)GetValue(MinLongitudeProperty); }
set { SetValue(MinLongitudeProperty, value); }
}
/// <summary>
/// Optional maximum longitude value. Default is NaN.
/// </summary>
public double MaxLongitude
{
get { return (double)GetValue(MaxLongitudeProperty); }
set { SetValue(MaxLongitudeProperty, value); }
}
/// <summary> /// <summary>
/// Optional minimum latitude value. Default is NaN. /// Optional minimum latitude value. Default is NaN.
/// </summary> /// </summary>
@ -130,6 +94,24 @@ namespace MapControl
set { SetValue(MaxLatitudeProperty, value); } set { SetValue(MaxLatitudeProperty, value); }
} }
/// <summary>
/// Optional minimum longitude value. Default is NaN.
/// </summary>
public double MinLongitude
{
get { return (double)GetValue(MinLongitudeProperty); }
set { SetValue(MinLongitudeProperty, value); }
}
/// <summary>
/// Optional maximum longitude value. Default is NaN.
/// </summary>
public double MaxLongitude
{
get { return (double)GetValue(MaxLongitudeProperty); }
set { SetValue(MaxLongitudeProperty, value); }
}
/// <summary> /// <summary>
/// Optional maximum width of the map image's bounding box. Default is NaN. /// Optional maximum width of the map image's bounding box. Default is NaN.
/// </summary> /// </summary>
@ -140,8 +122,8 @@ namespace MapControl
} }
/// <summary> /// <summary>
/// Relative size of the map image in relation to the current viewport size. /// Relative size of the map image in relation to the current viewport size.
/// Setting a value greater than one will let MapImageLayer request images that /// Setting a value greater than one will let ImageLayer request images that
/// are larger than the viewport, in order to support smooth panning. /// are larger than the viewport, in order to support smooth panning.
/// </summary> /// </summary>
public double RelativeImageSize public double RelativeImageSize
@ -159,42 +141,87 @@ namespace MapControl
set { SetValue(UpdateIntervalProperty, value); } set { SetValue(UpdateIntervalProperty, value); }
} }
protected BoundingBox ImageBoundingBox { get; private set; } /// <summary>
/// Controls if images are updated while the viewport is still changing.
protected virtual BoundingBox ProjectedBoundingBox /// </summary>
public bool UpdateWhileViewportChanging
{ {
get get { return (bool)GetValue(UpdateWhileViewportChangingProperty); }
{ set { SetValue(UpdateWhileViewportChangingProperty, value); }
var p1 = ParentMap.MapTransform.Transform(new Location(ImageBoundingBox.South, ImageBoundingBox.West)); }
var p2 = ParentMap.MapTransform.Transform(new Location(ImageBoundingBox.North, ImageBoundingBox.East));
return new BoundingBox( /// <summary>
TileSource.MetersPerDegree * p1.X, TileSource.MetersPerDegree * p2.X, /// Description of the ImageLayer.
TileSource.MetersPerDegree * p1.Y, TileSource.MetersPerDegree * p2.Y); /// Used to display copyright information on top of the map.
} /// </summary>
public string Description
{
get { return (string)GetValue(DescriptionProperty); }
set { SetValue(DescriptionProperty, value); }
}
/// <summary>
/// Optional foreground brush.
/// Sets MapBase.Foreground if not null and the ImageLayer is the base map layer.
/// </summary>
public Brush MapForeground
{
get { return (Brush)GetValue(MapForegroundProperty); }
set { SetValue(MapForegroundProperty, value); }
}
/// <summary>
/// Optional background brush.
/// Sets MapBase.Background if not null and the ImageLayer is the base map layer.
/// </summary>
public Brush MapBackground
{
get { return (Brush)GetValue(MapBackgroundProperty); }
set { SetValue(MapBackgroundProperty, value); }
} }
protected override void OnViewportChanged(ViewportChangedEventArgs e) protected override void OnViewportChanged(ViewportChangedEventArgs e)
{ {
base.OnViewportChanged(e); base.OnViewportChanged(e);
if (Math.Abs(e.OriginOffset) > 180d) if (e.ProjectionChanged)
{ {
var offset = 360d * Math.Sign(e.OriginOffset); UpdateImage((BitmapSource)null);
UpdateImage();
ImageBoundingBox = new BoundingBox(ImageBoundingBox.West + offset, ImageBoundingBox.East + offset, ImageBoundingBox.South, ImageBoundingBox.North); }
else
foreach (var mapImage in Children.OfType<MapImage>().Where(i => i.BoundingBoxValid)) {
if (Math.Abs(e.LongitudeOffset) > 180d && boundingBox != null && boundingBox.HasValidBounds)
{ {
mapImage.SetBoundingBox(mapImage.West + offset, mapImage.East + offset, mapImage.South, mapImage.North); var offset = 360d * Math.Sign(e.LongitudeOffset);
boundingBox.West += offset;
boundingBox.East += offset;
foreach (UIElement element in Children)
{
var bbox = GetBoundingBox(element);
if (bbox != null && bbox.HasValidBounds)
{
SetBoundingBox(element, new BoundingBox(bbox.South, bbox.West + offset, bbox.North, bbox.East + offset));
}
}
}
if (updateTimer.IsEnabled && !UpdateWhileViewportChanging)
{
updateTimer.Stop(); // restart
}
if (!updateTimer.IsEnabled)
{
updateTimer.Start();
} }
} }
updateTimer.Stop();
updateTimer.Start();
} }
protected void UpdateImage() protected virtual void UpdateImage()
{ {
updateTimer.Stop(); updateTimer.Stop();
@ -206,129 +233,99 @@ namespace MapControl
{ {
updateInProgress = true; updateInProgress = true;
var relativeSize = Math.Max(RelativeImageSize, 1d); var width = ParentMap.RenderSize.Width * RelativeImageSize;
var width = ParentMap.RenderSize.Width * relativeSize; var height = ParentMap.RenderSize.Height * RelativeImageSize;
var height = ParentMap.RenderSize.Height * relativeSize; var x = (ParentMap.RenderSize.Width - width) / 2d;
var dx = (ParentMap.RenderSize.Width - width) / 2d; var y = (ParentMap.RenderSize.Height - height) / 2d;
var dy = (ParentMap.RenderSize.Height - height) / 2d; var rect = new Rect(x, y, width, height);
var loc1 = ParentMap.ViewportPointToLocation(new Point(dx, dy)); boundingBox = ParentMap.MapProjection.ViewportRectToBoundingBox(rect);
var loc2 = ParentMap.ViewportPointToLocation(new Point(dx + width, dy));
var loc3 = ParentMap.ViewportPointToLocation(new Point(dx, dy + height));
var loc4 = ParentMap.ViewportPointToLocation(new Point(dx + width, dy + height));
var west = Math.Min(loc1.Longitude, Math.Min(loc2.Longitude, Math.Min(loc3.Longitude, loc4.Longitude))); if (boundingBox != null && boundingBox.HasValidBounds)
var east = Math.Max(loc1.Longitude, Math.Max(loc2.Longitude, Math.Max(loc3.Longitude, loc4.Longitude)));
var south = Math.Min(loc1.Latitude, Math.Min(loc2.Latitude, Math.Min(loc3.Latitude, loc4.Latitude)));
var north = Math.Max(loc1.Latitude, Math.Max(loc2.Latitude, Math.Max(loc3.Latitude, loc4.Latitude)));
if (!double.IsNaN(MinLongitude) && west < MinLongitude)
{ {
west = MinLongitude; if (!double.IsNaN(MinLatitude) && boundingBox.South < MinLatitude)
{
boundingBox.South = MinLatitude;
}
if (!double.IsNaN(MinLongitude) && boundingBox.West < MinLongitude)
{
boundingBox.West = MinLongitude;
}
if (!double.IsNaN(MaxLatitude) && boundingBox.North > MaxLatitude)
{
boundingBox.North = MaxLatitude;
}
if (!double.IsNaN(MaxLongitude) && boundingBox.East > MaxLongitude)
{
boundingBox.East = MaxLongitude;
}
if (!double.IsNaN(MaxBoundingBoxWidth) && boundingBox.Width > MaxBoundingBoxWidth)
{
var d = (boundingBox.Width - MaxBoundingBoxWidth) / 2d;
boundingBox.West += d;
boundingBox.East -= d;
}
} }
if (!double.IsNaN(MaxLongitude) && east > MaxLongitude) var imageUpdated = false;
try
{ {
east = MaxLongitude; imageUpdated = UpdateImage(boundingBox);
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
} }
if (!double.IsNaN(MinLatitude) && south < MinLatitude) if (!imageUpdated)
{ {
south = MinLatitude; UpdateImage((BitmapSource)null);
} }
if (!double.IsNaN(MaxLatitude) && north > MaxLatitude)
{
north = MaxLatitude;
}
if (!double.IsNaN(MaxBoundingBoxWidth) && east - west > MaxBoundingBoxWidth)
{
var d = (east - west - MaxBoundingBoxWidth) / 2d;
west += d;
east -= d;
}
ImageBoundingBox = new BoundingBox(west, east, south, north);
var p1 = ParentMap.MapTransform.Transform(new Location(south, west));
var p2 = ParentMap.MapTransform.Transform(new Location(north, east));
UpdateImage((int)Math.Round((p2.X - p1.X) * ParentMap.ViewportScale),
(int)Math.Round((p2.Y - p1.Y) * ParentMap.ViewportScale));
} }
} }
protected virtual void UpdateImage(int width, int height) /// <summary>
{ /// Creates an image request Uri or a BitmapSource for the specified image bounding box.
if (UriFormat != null && width > 0 && height > 0) /// Must either call UpdateImage(Uri) or UpdateImage(BitmapSource) or return false on failure.
{ /// </summary>
var uri = UriFormat protected abstract bool UpdateImage(BoundingBox boundingBox);
.Replace("{X}", width.ToString())
.Replace("{Y}", height.ToString());
if (uri.Contains("{W}") && uri.Contains("{E}") && uri.Contains("{S}") && uri.Contains("{N}"))
{
var projectedBoundingBox = ProjectedBoundingBox;
uri = uri
.Replace("{W}", projectedBoundingBox.West.ToString(CultureInfo.InvariantCulture))
.Replace("{S}", projectedBoundingBox.South.ToString(CultureInfo.InvariantCulture))
.Replace("{E}", projectedBoundingBox.East.ToString(CultureInfo.InvariantCulture))
.Replace("{N}", projectedBoundingBox.North.ToString(CultureInfo.InvariantCulture));
}
else
{
uri = uri
.Replace("{w}", ImageBoundingBox.West.ToString(CultureInfo.InvariantCulture))
.Replace("{s}", ImageBoundingBox.South.ToString(CultureInfo.InvariantCulture))
.Replace("{e}", ImageBoundingBox.East.ToString(CultureInfo.InvariantCulture))
.Replace("{n}", ImageBoundingBox.North.ToString(CultureInfo.InvariantCulture));
}
UpdateImage(new Uri(uri));
}
else
{
UpdateImage((BitmapSource)null);
}
}
private void SetTopImage(BitmapSource bitmap) private void SetTopImage(BitmapSource bitmap)
{ {
currentImageIndex = (currentImageIndex + 1) % 2; topImageIndex = (topImageIndex + 1) % 2;
var topImage = (MapImage)Children[currentImageIndex]; var topImage = (Image)Children[topImageIndex];
topImage.SetBoundingBox(ImageBoundingBox.West, ImageBoundingBox.East, ImageBoundingBox.South, ImageBoundingBox.North);
topImage.Source = bitmap; topImage.Source = bitmap;
SetBoundingBox(topImage, boundingBox?.Clone());
} }
private void SwapImages() private void SwapImages()
{ {
var topImage = (MapImage)Children[currentImageIndex]; var topImage = (Image)Children[topImageIndex];
var bottomImage = (MapImage)Children[(currentImageIndex + 1) % 2]; var bottomImage = (Image)Children[(topImageIndex + 1) % 2];
Canvas.SetZIndex(topImage, 1); Canvas.SetZIndex(topImage, 1);
Canvas.SetZIndex(bottomImage, 0); Canvas.SetZIndex(bottomImage, 0);
if (topImage.Source != null) if (topImage.Source != null)
{ {
var fadeAnimation = new DoubleAnimation topImage.BeginAnimation(OpacityProperty, new DoubleAnimation
{ {
From = 0d,
To = 1d, To = 1d,
Duration = Tile.FadeDuration, Duration = Tile.FadeDuration
FillBehavior = FillBehavior.Stop });
};
fadeAnimation.Completed += (s, e) => bottomImage.BeginAnimation(OpacityProperty, new DoubleAnimation
{ {
bottomImage.Opacity = 0d; To = 0d,
bottomImage.Source = null; BeginTime = Tile.FadeDuration,
}; Duration = TimeSpan.Zero
});
topImage.BeginAnimation(UIElement.OpacityProperty, fadeAnimation);
topImage.Opacity = 1d;
} }
else else
{ {

View file

@ -1,5 +1,5 @@
// XAML Map Control - http://xamlmapcontrol.codeplex.com/ // XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2016 Clemens Fischer // © 2017 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL) // Licensed under the Microsoft Public License (Ms-PL)
#if NETFX_CORE #if NETFX_CORE

View file

@ -1,5 +1,5 @@
// XAML Map Control - http://xamlmapcontrol.codeplex.com/ // XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2016 Clemens Fischer // © 2017 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL) // Licensed under the Microsoft Public License (Ms-PL)
using System.Windows; using System.Windows;
@ -14,12 +14,10 @@ namespace MapControl
{ {
static MapItem() static MapItem()
{ {
DefaultStyleKeyProperty.OverrideMetadata( DefaultStyleKeyProperty.OverrideMetadata(typeof(MapItem), new FrameworkPropertyMetadata(typeof(MapItem)));
typeof(MapItem), new FrameworkPropertyMetadata(typeof(MapItem)));
} }
public static readonly DependencyProperty LocationProperty = public static readonly DependencyProperty LocationProperty = MapPanel.LocationProperty.AddOwner(typeof(MapItem));
MapPanel.LocationProperty.AddOwner(typeof(MapItem));
public Location Location public Location Location
{ {

View file

@ -1,5 +1,5 @@
// XAML Map Control - http://xamlmapcontrol.codeplex.com/ // XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2016 Clemens Fischer // © 2017 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL) // Licensed under the Microsoft Public License (Ms-PL)
#if NETFX_CORE #if NETFX_CORE

View file

@ -1,5 +1,5 @@
// XAML Map Control - http://xamlmapcontrol.codeplex.com/ // XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2016 Clemens Fischer // © 2017 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL) // Licensed under the Microsoft Public License (Ms-PL)
using System.Windows; using System.Windows;
@ -14,8 +14,7 @@ namespace MapControl
{ {
static MapItemsControl() static MapItemsControl()
{ {
DefaultStyleKeyProperty.OverrideMetadata( DefaultStyleKeyProperty.OverrideMetadata(typeof(MapItemsControl), new FrameworkPropertyMetadata(typeof(MapItemsControl)));
typeof(MapItemsControl), new FrameworkPropertyMetadata(typeof(MapItemsControl)));
} }
protected override DependencyObject GetContainerForItemOverride() protected override DependencyObject GetContainerForItemOverride()

View file

@ -1,5 +1,5 @@
// XAML Map Control - http://xamlmapcontrol.codeplex.com/ // XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2016 Clemens Fischer // © 2017 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL) // Licensed under the Microsoft Public License (Ms-PL)
#if NETFX_CORE #if NETFX_CORE
@ -23,49 +23,49 @@ namespace MapControl
public partial class MapOverlay public partial class MapOverlay
{ {
public static readonly DependencyProperty FontSizeProperty = DependencyProperty.Register( public static readonly DependencyProperty FontSizeProperty = DependencyProperty.Register(
"FontSize", typeof(double), typeof(MapOverlay), new PropertyMetadata(10d)); nameof(FontSize), typeof(double), typeof(MapOverlay), new PropertyMetadata(10d));
public static readonly DependencyProperty FontFamilyProperty = DependencyProperty.Register( public static readonly DependencyProperty FontFamilyProperty = DependencyProperty.Register(
"FontFamily", typeof(FontFamily), typeof(MapOverlay), new PropertyMetadata(null)); nameof(FontFamily), typeof(FontFamily), typeof(MapOverlay), new PropertyMetadata(null));
public static readonly DependencyProperty FontStyleProperty = DependencyProperty.Register( public static readonly DependencyProperty FontStyleProperty = DependencyProperty.Register(
"FontStyle", typeof(FontStyle), typeof(MapOverlay), new PropertyMetadata(FontStyles.Normal)); nameof(FontStyle), typeof(FontStyle), typeof(MapOverlay), new PropertyMetadata(FontStyles.Normal));
public static readonly DependencyProperty FontStretchProperty = DependencyProperty.Register( public static readonly DependencyProperty FontStretchProperty = DependencyProperty.Register(
"FontStretch", typeof(FontStretch), typeof(MapOverlay), new PropertyMetadata(FontStretches.Normal)); nameof(FontStretch), typeof(FontStretch), typeof(MapOverlay), new PropertyMetadata(FontStretches.Normal));
public static readonly DependencyProperty FontWeightProperty = DependencyProperty.Register( public static readonly DependencyProperty FontWeightProperty = DependencyProperty.Register(
"FontWeight", typeof(FontWeight), typeof(MapOverlay), new PropertyMetadata(FontWeights.Normal)); nameof(FontWeight), typeof(FontWeight), typeof(MapOverlay), new PropertyMetadata(FontWeights.Normal));
public static readonly DependencyProperty ForegroundProperty = DependencyProperty.Register( public static readonly DependencyProperty ForegroundProperty = DependencyProperty.Register(
"Foreground", typeof(Brush), typeof(MapOverlay), new PropertyMetadata(null)); nameof(Foreground), typeof(Brush), typeof(MapOverlay), new PropertyMetadata(null));
public static readonly DependencyProperty StrokeProperty = DependencyProperty.Register( public static readonly DependencyProperty StrokeProperty = DependencyProperty.Register(
"Stroke", typeof(Brush), typeof(MapOverlay), new PropertyMetadata(null)); nameof(Stroke), typeof(Brush), typeof(MapOverlay), new PropertyMetadata(null));
public static readonly DependencyProperty StrokeThicknessProperty = DependencyProperty.Register( public static readonly DependencyProperty StrokeThicknessProperty = DependencyProperty.Register(
"StrokeThickness", typeof(double), typeof(MapOverlay), new PropertyMetadata(1d)); nameof(StrokeThickness), typeof(double), typeof(MapOverlay), new PropertyMetadata(1d));
public static readonly DependencyProperty StrokeDashArrayProperty = DependencyProperty.Register( public static readonly DependencyProperty StrokeDashArrayProperty = DependencyProperty.Register(
"StrokeDashArray", typeof(DoubleCollection), typeof(MapOverlay), new PropertyMetadata(null)); nameof(StrokeDashArray), typeof(DoubleCollection), typeof(MapOverlay), new PropertyMetadata(null));
public static readonly DependencyProperty StrokeDashOffsetProperty = DependencyProperty.Register( public static readonly DependencyProperty StrokeDashOffsetProperty = DependencyProperty.Register(
"StrokeDashOffset", typeof(double), typeof(MapOverlay), new PropertyMetadata(0d)); nameof(StrokeDashOffset), typeof(double), typeof(MapOverlay), new PropertyMetadata(0d));
public static readonly DependencyProperty StrokeDashCapProperty = DependencyProperty.Register( public static readonly DependencyProperty StrokeDashCapProperty = DependencyProperty.Register(
"StrokeDashCap", typeof(PenLineCap), typeof(MapOverlay), new PropertyMetadata(PenLineCap.Flat)); nameof(StrokeDashCap), typeof(PenLineCap), typeof(MapOverlay), new PropertyMetadata(PenLineCap.Flat));
public static readonly DependencyProperty StrokeStartLineCapProperty = DependencyProperty.Register( public static readonly DependencyProperty StrokeStartLineCapProperty = DependencyProperty.Register(
"StrokeStartLineCap", typeof(PenLineCap), typeof(MapOverlay), new PropertyMetadata(PenLineCap.Flat)); nameof(StrokeStartLineCap), typeof(PenLineCap), typeof(MapOverlay), new PropertyMetadata(PenLineCap.Flat));
public static readonly DependencyProperty StrokeEndLineCapProperty = DependencyProperty.Register( public static readonly DependencyProperty StrokeEndLineCapProperty = DependencyProperty.Register(
"StrokeEndLineCap", typeof(PenLineCap), typeof(MapOverlay), new PropertyMetadata(PenLineCap.Flat)); nameof(StrokeEndLineCap), typeof(PenLineCap), typeof(MapOverlay), new PropertyMetadata(PenLineCap.Flat));
public static readonly DependencyProperty StrokeLineJoinProperty = DependencyProperty.Register( public static readonly DependencyProperty StrokeLineJoinProperty = DependencyProperty.Register(
"StrokeLineJoin", typeof(PenLineJoin), typeof(MapOverlay), new PropertyMetadata(PenLineJoin.Miter)); nameof(StrokeLineJoin), typeof(PenLineJoin), typeof(MapOverlay), new PropertyMetadata(PenLineJoin.Miter));
public static readonly DependencyProperty StrokeMiterLimitProperty = DependencyProperty.Register( public static readonly DependencyProperty StrokeMiterLimitProperty = DependencyProperty.Register(
"StrokeMiterLimit", typeof(double), typeof(MapOverlay), new PropertyMetadata(1d)); nameof(StrokeMiterLimit), typeof(double), typeof(MapOverlay), new PropertyMetadata(1d));
protected override void SetParentMapOverride(MapBase parentMap) protected override void SetParentMapOverride(MapBase parentMap)
{ {

View file

@ -1,5 +1,5 @@
// XAML Map Control - http://xamlmapcontrol.codeplex.com/ // XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2016 Clemens Fischer // © 2017 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL) // Licensed under the Microsoft Public License (Ms-PL)
using System.Windows; using System.Windows;

View file

@ -1,5 +1,5 @@
// XAML Map Control - http://xamlmapcontrol.codeplex.com/ // XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2016 Clemens Fischer // © 2017 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL) // Licensed under the Microsoft Public License (Ms-PL)
#if NETFX_CORE #if NETFX_CORE
@ -13,7 +13,7 @@ using System.Windows.Media;
namespace MapControl namespace MapControl
{ {
/// <summary> /// <summary>
/// Base class for map overlays with font, background, foreground and stroke properties. /// Base class for map overlays with background, foreground, stroke and font properties.
/// </summary> /// </summary>
public partial class MapOverlay : MapPanel public partial class MapOverlay : MapPanel
{ {

View file

@ -1,5 +1,5 @@
// XAML Map Control - http://xamlmapcontrol.codeplex.com/ // XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2016 Clemens Fischer // © 2017 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL) // Licensed under the Microsoft Public License (Ms-PL)
#if NETFX_CORE #if NETFX_CORE

View file

@ -1,5 +1,5 @@
// XAML Map Control - http://xamlmapcontrol.codeplex.com/ // XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2016 Clemens Fischer // © 2017 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL) // Licensed under the Microsoft Public License (Ms-PL)
using System.Windows; using System.Windows;

View file

@ -1,29 +1,40 @@
// XAML Map Control - http://xamlmapcontrol.codeplex.com/ // XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2016 Clemens Fischer // © 2017 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL) // Licensed under the Microsoft Public License (Ms-PL)
using System; using System;
#if NETFX_CORE #if NETFX_CORE
using Windows.Foundation; using Windows.Foundation;
using Windows.UI.Xaml; using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media; using Windows.UI.Xaml.Media;
#else #else
using System.Windows; using System.Windows;
using System.Windows.Controls;
using System.Windows.Media; using System.Windows.Media;
#endif #endif
namespace MapControl namespace MapControl
{ {
public interface IMapElement
{
MapBase ParentMap { get; set; }
}
/// <summary> /// <summary>
/// Arranges child elements on a Map at positions specified by the attached property Location. /// Arranges child elements on a Map at positions specified by the attached property Location,
/// The Location value is transformed to a viewport position and assigned as TranslateTransform /// or in rectangles specified by the attached property BoundingBox.
/// to the RenderTransform of the element, either directly or as last child of a TransformGroup. /// An element's viewport position is assigned as TranslateTransform to its RenderTransform property,
/// either directly or as last child of a TransformGroup.
/// </summary> /// </summary>
public partial class MapPanel : PanelBase, IMapElement public partial class MapPanel : Panel, IMapElement
{ {
public static readonly DependencyProperty LocationProperty = DependencyProperty.RegisterAttached( public static readonly DependencyProperty LocationProperty = DependencyProperty.RegisterAttached(
"Location", typeof(Location), typeof(MapPanel), new PropertyMetadata(null, LocationPropertyChanged)); "Location", typeof(Location), typeof(MapPanel), new PropertyMetadata(null, LocationPropertyChanged));
public static readonly DependencyProperty BoundingBoxProperty = DependencyProperty.RegisterAttached(
"BoundingBox", typeof(BoundingBox), typeof(MapPanel), new PropertyMetadata(null, BoundingBoxPropertyChanged));
public static Location GetLocation(UIElement element) public static Location GetLocation(UIElement element)
{ {
return (Location)element.GetValue(LocationProperty); return (Location)element.GetValue(LocationProperty);
@ -34,6 +45,16 @@ namespace MapControl
element.SetValue(LocationProperty, value); element.SetValue(LocationProperty, value);
} }
public static BoundingBox GetBoundingBox(UIElement element)
{
return (BoundingBox)element.GetValue(BoundingBoxProperty);
}
public static void SetBoundingBox(UIElement element, BoundingBox value)
{
element.SetValue(BoundingBoxProperty, value);
}
private MapBase parentMap; private MapBase parentMap;
public MapBase ParentMap public MapBase ParentMap
@ -42,20 +63,38 @@ namespace MapControl
set { SetParentMapOverride(value); } set { SetParentMapOverride(value); }
} }
protected override Size MeasureOverride(Size availableSize)
{
availableSize = new Size(double.PositiveInfinity, double.PositiveInfinity);
foreach (UIElement element in Children)
{
element.Measure(availableSize);
}
return new Size();
}
protected override Size ArrangeOverride(Size finalSize) protected override Size ArrangeOverride(Size finalSize)
{ {
foreach (UIElement element in Children) foreach (UIElement element in Children)
{ {
var location = GetLocation(element); BoundingBox boundingBox;
Location location;
if (location != null) if ((boundingBox = GetBoundingBox(element)) != null)
{
ArrangeElementWithBoundingBox(element);
SetBoundingBoxRect(element, parentMap, boundingBox);
}
else if ((location = GetLocation(element)) != null)
{ {
ArrangeElementWithLocation(element); ArrangeElementWithLocation(element);
SetViewportPosition(element, parentMap, location); SetViewportPosition(element, parentMap, location);
} }
else else
{ {
ArrangeElementWithoutLocation(element, finalSize); ArrangeElement(element, finalSize);
} }
} }
@ -74,7 +113,7 @@ namespace MapControl
if (parentMap != null && parentMap != this) if (parentMap != null && parentMap != this)
{ {
parentMap.ViewportChanged += OnViewportChanged; parentMap.ViewportChanged += OnViewportChanged;
OnViewportChanged(new ViewportChangedEventArgs(0d)); OnViewportChanged(new ViewportChangedEventArgs());
} }
} }
@ -82,9 +121,14 @@ namespace MapControl
{ {
foreach (UIElement element in Children) foreach (UIElement element in Children)
{ {
var location = GetLocation(element); BoundingBox boundingBox;
Location location;
if (location != null) if ((boundingBox = GetBoundingBox(element)) != null)
{
SetBoundingBoxRect(element, parentMap, boundingBox);
}
else if ((location = GetLocation(element)) != null)
{ {
SetViewportPosition(element, parentMap, location); SetViewportPosition(element, parentMap, location);
} }
@ -109,17 +153,37 @@ namespace MapControl
private static void LocationPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e) private static void LocationPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{ {
var element = (UIElement)obj; var element = (UIElement)obj;
var map = GetParentMap(element);
var location = (Location)e.NewValue;
if (e.NewValue == null) if (location == null)
{ {
ArrangeElementWithoutLocation(element, Size.Empty); ArrangeElement(element, map?.RenderSize ?? new Size());
} }
else if (e.OldValue == null) else if (e.OldValue == null)
{ {
ArrangeElementWithLocation(element); ArrangeElementWithLocation(element); // arrange once when Location was null before
} }
SetViewportPosition(element, GetParentMap(element), (Location)e.NewValue); SetViewportPosition(element, map, location);
}
private static void BoundingBoxPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
var element = (FrameworkElement)obj;
var map = GetParentMap(element);
var boundingBox = (BoundingBox)e.NewValue;
if (boundingBox == null)
{
ArrangeElement(element, map?.RenderSize ?? new Size());
}
else if (e.OldValue == null)
{
ArrangeElementWithBoundingBox(element); // arrange once when BoundingBox was null before
}
SetBoundingBoxRect(element, map, boundingBox);
} }
private static void SetViewportPosition(UIElement element, MapBase parentMap, Location location) private static void SetViewportPosition(UIElement element, MapBase parentMap, Location location)
@ -128,12 +192,12 @@ namespace MapControl
if (parentMap != null && location != null) if (parentMap != null && location != null)
{ {
viewportPosition = parentMap.LocationToViewportPoint(location); viewportPosition = parentMap.MapProjection.LocationToViewportPoint(location);
if (viewportPosition.X < 0d || viewportPosition.X > parentMap.RenderSize.Width || if (viewportPosition.X < 0d || viewportPosition.X > parentMap.RenderSize.Width ||
viewportPosition.Y < 0d || viewportPosition.Y > parentMap.RenderSize.Height) viewportPosition.Y < 0d || viewportPosition.Y > parentMap.RenderSize.Height)
{ {
viewportPosition = parentMap.LocationToViewportPoint(new Location( viewportPosition = parentMap.MapProjection.LocationToViewportPoint(new Location(
location.Latitude, location.Latitude,
Location.NearestLongitude(location.Longitude, parentMap.Center.Longitude))); Location.NearestLongitude(location.Longitude, parentMap.Center.Longitude)));
} }
@ -145,14 +209,6 @@ namespace MapControl
} }
} }
var translateTransform = GetTranslateTransform(element);
translateTransform.X = viewportPosition.X;
translateTransform.Y = viewportPosition.Y;
}
private static TranslateTransform GetTranslateTransform(UIElement element)
{
var translateTransform = element.RenderTransform as TranslateTransform; var translateTransform = element.RenderTransform as TranslateTransform;
if (translateTransform == null) if (translateTransform == null)
@ -179,17 +235,85 @@ namespace MapControl
} }
} }
return translateTransform; translateTransform.X = viewportPosition.X;
translateTransform.Y = viewportPosition.Y;
}
private static void SetBoundingBoxRect(UIElement element, MapBase parentMap, BoundingBox boundingBox)
{
var rotation = 0d;
var viewportPosition = new Point();
if (parentMap != null && boundingBox != null)
{
var projection = parentMap.MapProjection;
var rect = projection.BoundingBoxToRect(boundingBox);
var center = new Point(rect.X + rect.Width / 2d, rect.Y + rect.Height / 2d);
rotation = parentMap.Heading;
viewportPosition = projection.ViewportTransform.Transform(center);
if (viewportPosition.X < 0d || viewportPosition.X > parentMap.RenderSize.Width ||
viewportPosition.Y < 0d || viewportPosition.Y > parentMap.RenderSize.Height)
{
var location = projection.PointToLocation(center);
location.Longitude = Location.NearestLongitude(location.Longitude, parentMap.Center.Longitude);
viewportPosition = projection.LocationToViewportPoint(location);
}
var width = rect.Width * projection.ViewportScale;
var height = rect.Height * projection.ViewportScale;
if (element is FrameworkElement)
{
((FrameworkElement)element).Width = width;
((FrameworkElement)element).Height = height;
}
else
{
element.Arrange(new Rect(-width / 2d, -height / 2d, width, height)); // ???
}
}
var transformGroup = element.RenderTransform as TransformGroup;
RotateTransform rotateTransform;
TranslateTransform translateTransform;
if (transformGroup == null ||
transformGroup.Children.Count != 2 ||
(rotateTransform = transformGroup.Children[0] as RotateTransform) == null ||
(translateTransform = transformGroup.Children[1] as TranslateTransform) == null)
{
transformGroup = new TransformGroup();
rotateTransform = new RotateTransform();
translateTransform = new TranslateTransform();
transformGroup.Children.Add(rotateTransform);
transformGroup.Children.Add(translateTransform);
element.RenderTransform = transformGroup;
element.RenderTransformOrigin = new Point(0.5, 0.5);
}
rotateTransform.Angle = rotation;
translateTransform.X = viewportPosition.X;
translateTransform.Y = viewportPosition.Y;
}
private static void ArrangeElementWithBoundingBox(UIElement element)
{
var size = element.DesiredSize;
element.Arrange(new Rect(-size.Width / 2d, -size.Height / 2d, size.Width, size.Height));
} }
private static void ArrangeElementWithLocation(UIElement element) private static void ArrangeElementWithLocation(UIElement element)
{ {
var rect = new Rect(new Point(), element.DesiredSize); var rect = new Rect(new Point(), element.DesiredSize);
var frameworkElement = element as FrameworkElement;
if (frameworkElement != null) if (element is FrameworkElement)
{ {
switch (frameworkElement.HorizontalAlignment) switch (((FrameworkElement)element).HorizontalAlignment)
{ {
case HorizontalAlignment.Center: case HorizontalAlignment.Center:
rect.X = -rect.Width / 2d; rect.X = -rect.Width / 2d;
@ -203,7 +327,7 @@ namespace MapControl
break; break;
} }
switch (frameworkElement.VerticalAlignment) switch (((FrameworkElement)element).VerticalAlignment)
{ {
case VerticalAlignment.Center: case VerticalAlignment.Center:
rect.Y = -rect.Height / 2d; rect.Y = -rect.Height / 2d;
@ -221,20 +345,13 @@ namespace MapControl
element.Arrange(rect); element.Arrange(rect);
} }
private static void ArrangeElementWithoutLocation(UIElement element, Size parentSize) private static void ArrangeElement(UIElement element, Size parentSize)
{ {
var rect = new Rect(new Point(), element.DesiredSize); var rect = new Rect(new Point(), element.DesiredSize);
var frameworkElement = element as FrameworkElement;
if (frameworkElement != null) if (element is FrameworkElement)
{ {
if (parentSize.IsEmpty) switch (((FrameworkElement)element).HorizontalAlignment)
{
var parent = frameworkElement.Parent as UIElement;
parentSize = parent?.RenderSize ?? new Size();
}
switch (frameworkElement.HorizontalAlignment)
{ {
case HorizontalAlignment.Center: case HorizontalAlignment.Center:
rect.X = (parentSize.Width - rect.Width) / 2d; rect.X = (parentSize.Width - rect.Width) / 2d;
@ -252,7 +369,7 @@ namespace MapControl
break; break;
} }
switch (frameworkElement.VerticalAlignment) switch (((FrameworkElement)element).VerticalAlignment)
{ {
case VerticalAlignment.Center: case VerticalAlignment.Center:
rect.Y = (parentSize.Height - rect.Height) / 2d; rect.Y = (parentSize.Height - rect.Height) / 2d;

View file

@ -1,5 +1,5 @@
// XAML Map Control - http://xamlmapcontrol.codeplex.com/ // XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2016 Clemens Fischer // © 2017 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL) // Licensed under the Microsoft Public License (Ms-PL)
#if NETFX_CORE #if NETFX_CORE
@ -33,8 +33,17 @@ namespace MapControl
// Workaround for missing PropertyChangedCallback for the Data property. // Workaround for missing PropertyChangedCallback for the Data property.
if (data != Data) if (data != Data)
{ {
if (data != null)
{
data.ClearValue(Geometry.TransformProperty);
}
data = Data; data = Data;
UpdateData();
if (data != null)
{
data.Transform = viewportTransform;
}
} }
// Path.MeasureOverride in Windows Runtime sometimes returns an empty Size, // Path.MeasureOverride in Windows Runtime sometimes returns an empty Size,

View file

@ -1,5 +1,5 @@
// XAML Map Control - http://xamlmapcontrol.codeplex.com/ // XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2016 Clemens Fischer // © 2017 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL) // Licensed under the Microsoft Public License (Ms-PL)
using System.Windows; using System.Windows;
@ -11,7 +11,7 @@ namespace MapControl
public partial class MapPath : Shape public partial class MapPath : Shape
{ {
public static readonly DependencyProperty DataProperty = DependencyProperty.Register( public static readonly DependencyProperty DataProperty = DependencyProperty.Register(
"Data", typeof(Geometry), typeof(MapPath), new FrameworkPropertyMetadata( nameof(Data), typeof(Geometry), typeof(MapPath), new FrameworkPropertyMetadata(
null, FrameworkPropertyMetadataOptions.AffectsRender, DataPropertyChanged, CoerceDataProperty)); null, FrameworkPropertyMetadataOptions.AffectsRender, DataPropertyChanged, CoerceDataProperty));
static MapPath() static MapPath()
@ -41,13 +41,24 @@ namespace MapControl
{ {
if (!ReferenceEquals(e.OldValue, e.NewValue)) if (!ReferenceEquals(e.OldValue, e.NewValue))
{ {
((MapPath)obj).UpdateData(); var mapPath = (MapPath)obj;
if (e.OldValue != null)
{
((Geometry)e.OldValue).ClearValue(Geometry.TransformProperty);
}
if (e.NewValue != null)
{
((Geometry)e.NewValue).Transform = mapPath.viewportTransform;
}
} }
} }
private static object CoerceDataProperty(DependencyObject obj, object value) private static object CoerceDataProperty(DependencyObject obj, object value)
{ {
var data = (Geometry)value; var data = (Geometry)value;
return (data != null && data.IsFrozen) ? data.CloneCurrentValue() : data; return (data != null && data.IsFrozen) ? data.CloneCurrentValue() : data;
} }
} }

View file

@ -1,8 +1,7 @@
// XAML Map Control - http://xamlmapcontrol.codeplex.com/ // XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2016 Clemens Fischer // © 2017 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL) // Licensed under the Microsoft Public License (Ms-PL)
using System;
#if NETFX_CORE #if NETFX_CORE
using Windows.UI.Xaml; using Windows.UI.Xaml;
using Windows.UI.Xaml.Media; using Windows.UI.Xaml.Media;
@ -22,11 +21,8 @@ namespace MapControl
public partial class MapPath : IMapElement public partial class MapPath : IMapElement
{ {
public static readonly DependencyProperty LocationProperty = DependencyProperty.Register( public static readonly DependencyProperty LocationProperty = DependencyProperty.Register(
"Location", typeof(Location), typeof(MapPath), nameof(Location), typeof(Location), typeof(MapPath),
new PropertyMetadata(null, (o, e) => ((MapPath)o).LocationChanged((Location)e.OldValue, (Location)e.NewValue))); new PropertyMetadata(null, (o, e) => ((MapPath)o).LocationPropertyChanged()));
private readonly TransformGroup viewportTransform = new TransformGroup();
private MapBase parentMap;
public Location Location public Location Location
{ {
@ -34,19 +30,17 @@ namespace MapControl
set { SetValue(LocationProperty, value); } set { SetValue(LocationProperty, value); }
} }
public TransformGroup ViewportTransform private readonly TransformGroup viewportTransform = new TransformGroup();
{ private MapBase parentMap;
get { return viewportTransform; }
}
public MapBase ParentMap public MapBase ParentMap
{ {
get { return parentMap; } get { return parentMap; }
set set
{ {
if (parentMap != null && Location != null) if (parentMap != null)
{ {
DetachViewportChanged(); parentMap.ViewportChanged -= OnViewportChanged;
} }
viewportTransform.Children.Clear(); viewportTransform.Children.Clear();
@ -54,12 +48,9 @@ namespace MapControl
if (parentMap != null) if (parentMap != null)
{ {
viewportTransform.Children.Add(parentMap.ViewportTransform); viewportTransform.Children.Add(new TranslateTransform());
viewportTransform.Children.Add(parentMap.MapProjection.ViewportTransform);
if (Location != null) parentMap.ViewportChanged += OnViewportChanged;
{
AttachViewportChanged();
}
} }
UpdateData(); UpdateData();
@ -68,52 +59,54 @@ namespace MapControl
protected virtual void UpdateData() protected virtual void UpdateData()
{ {
if (Data != null) }
protected virtual void OnViewportChanged(ViewportChangedEventArgs e)
{
double longitudeScale = parentMap.MapProjection.LongitudeScale;
if (e.ProjectionChanged)
{ {
Data.Transform = viewportTransform; viewportTransform.Children[1] = parentMap.MapProjection.ViewportTransform;
}
if (e.ProjectionChanged || double.IsNaN(longitudeScale))
{
UpdateData();
}
if (!double.IsNaN(longitudeScale)) // a normal cylindrical projection
{
var longitudeOffset = 0d;
if (Location != null)
{
var viewportPosition = parentMap.MapProjection.LocationToViewportPoint(Location);
if (viewportPosition.X < 0d || viewportPosition.X > parentMap.RenderSize.Width ||
viewportPosition.Y < 0d || viewportPosition.Y > parentMap.RenderSize.Height)
{
var nearestLongitude = Location.NearestLongitude(Location.Longitude, parentMap.Center.Longitude);
longitudeOffset = longitudeScale * (nearestLongitude - Location.Longitude);
}
}
((TranslateTransform)viewportTransform.Children[0]).X = longitudeOffset;
} }
} }
private void LocationChanged(Location oldValue, Location newValue) private void OnViewportChanged(object sender, ViewportChangedEventArgs e)
{
OnViewportChanged(e);
}
private void LocationPropertyChanged()
{ {
if (parentMap != null) if (parentMap != null)
{ {
if (oldValue == null) OnViewportChanged(new ViewportChangedEventArgs());
{
AttachViewportChanged();
}
else if (newValue == null)
{
DetachViewportChanged();
}
} }
} }
private void AttachViewportChanged()
{
viewportTransform.Children.Insert(0, new TranslateTransform());
parentMap.ViewportChanged += OnViewportChanged;
OnViewportChanged(parentMap, null);
}
private void DetachViewportChanged()
{
parentMap.ViewportChanged -= OnViewportChanged;
viewportTransform.Children.RemoveAt(0);
}
private void OnViewportChanged(object sender, EventArgs e)
{
var viewportPosition = parentMap.LocationToViewportPoint(Location);
var longitudeOffset = 0d;
if (viewportPosition.X < 0d || viewportPosition.X > parentMap.RenderSize.Width ||
viewportPosition.Y < 0d || viewportPosition.Y > parentMap.RenderSize.Height)
{
longitudeOffset = Location.NearestLongitude(Location.Longitude, parentMap.Center.Longitude) - Location.Longitude;
}
((TranslateTransform)viewportTransform.Children[0]).X = longitudeOffset;
}
} }
} }

View file

@ -1,5 +1,5 @@
// XAML Map Control - http://xamlmapcontrol.codeplex.com/ // XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2016 Clemens Fischer // © 2017 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL) // Licensed under the Microsoft Public License (Ms-PL)
using System.Linq; using System.Linq;
@ -16,12 +16,12 @@ namespace MapControl
public partial class MapPolyline public partial class MapPolyline
{ {
public static readonly DependencyProperty FillRuleProperty = DependencyProperty.Register( public static readonly DependencyProperty FillRuleProperty = DependencyProperty.Register(
"FillRule", typeof(FillRule), typeof(MapPolyline), nameof(FillRule), typeof(FillRule), typeof(MapPolyline),
new PropertyMetadata(FillRule.EvenOdd, (o, e) => ((PathGeometry)((MapPolyline)o).Data).FillRule = (FillRule)e.NewValue)); new PropertyMetadata(FillRule.EvenOdd, (o, e) => ((PathGeometry)((MapPolyline)o).Data).FillRule = (FillRule)e.NewValue));
public MapPolyline() public MapPolyline()
{ {
Data = new PathGeometry { Transform = ViewportTransform }; Data = new PathGeometry();
} }
protected override void UpdateData() protected override void UpdateData()
@ -31,7 +31,7 @@ namespace MapControl
if (ParentMap != null && Locations != null && Locations.Any()) if (ParentMap != null && Locations != null && Locations.Any())
{ {
var points = Locations.Select(l => ParentMap.MapTransform.Transform(l)); var points = Locations.Select(l => ParentMap.MapProjection.LocationToPoint(l));
var figure = new PathFigure var figure = new PathFigure
{ {

View file

@ -1,5 +1,5 @@
// XAML Map Control - http://xamlmapcontrol.codeplex.com/ // XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2016 Clemens Fischer // © 2017 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL) // Licensed under the Microsoft Public License (Ms-PL)
using System.Linq; using System.Linq;
@ -16,7 +16,7 @@ namespace MapControl
public MapPolyline() public MapPolyline()
{ {
Data = new StreamGeometry { Transform = ViewportTransform }; Data = new StreamGeometry();
} }
protected override void UpdateData() protected override void UpdateData()
@ -27,7 +27,7 @@ namespace MapControl
{ {
using (var context = geometry.Open()) using (var context = geometry.Open())
{ {
var points = Locations.Select(l => ParentMap.MapTransform.Transform(l)); var points = Locations.Select(l => ParentMap.MapProjection.LocationToPoint(l));
context.BeginFigure(points.First(), IsClosed, IsClosed); context.BeginFigure(points.First(), IsClosed, IsClosed);
context.PolyLineTo(points.Skip(1).ToList(), true, false); context.PolyLineTo(points.Skip(1).ToList(), true, false);

View file

@ -1,11 +1,10 @@
// XAML Map Control - http://xamlmapcontrol.codeplex.com/ // XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2016 Clemens Fischer // © 2017 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL) // Licensed under the Microsoft Public License (Ms-PL)
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Specialized; using System.Collections.Specialized;
#if NETFX_CORE #if NETFX_CORE
using System.Collections;
using Windows.UI.Xaml; using Windows.UI.Xaml;
using Windows.UI.Xaml.Media; using Windows.UI.Xaml.Media;
#else #else
@ -21,18 +20,12 @@ namespace MapControl
/// </summary> /// </summary>
public partial class MapPolyline : MapPath public partial class MapPolyline : MapPath
{ {
#if NETFX_CORE
// Binding fails on Windows Runtime when property type is IEnumerable<Location>
public static readonly DependencyProperty LocationsProperty = DependencyProperty.Register( public static readonly DependencyProperty LocationsProperty = DependencyProperty.Register(
"Locations", typeof(IEnumerable), typeof(MapPolyline), nameof(Locations), typeof(IEnumerable<Location>), typeof(MapPolyline),
new PropertyMetadata(null, (o, e) => ((MapPolyline)o).LocationsChanged(e.OldValue as INotifyCollectionChanged, e.NewValue as INotifyCollectionChanged))); new PropertyMetadata(null, (o, e) => ((MapPolyline)o).LocationsPropertyChanged(e)));
#else
public static readonly DependencyProperty LocationsProperty = DependencyProperty.Register(
"Locations", typeof(IEnumerable<Location>), typeof(MapPolyline),
new PropertyMetadata(null, (o, e) => ((MapPolyline)o).LocationsChanged(e.OldValue as INotifyCollectionChanged, e.NewValue as INotifyCollectionChanged)));
#endif
public static readonly DependencyProperty IsClosedProperty = DependencyProperty.Register( public static readonly DependencyProperty IsClosedProperty = DependencyProperty.Register(
"IsClosed", typeof(bool), typeof(MapPolyline), nameof(IsClosed), typeof(bool), typeof(MapPolyline),
new PropertyMetadata(false, (o, e) => ((MapPolyline)o).UpdateData())); new PropertyMetadata(false, (o, e) => ((MapPolyline)o).UpdateData()));
/// <summary> /// <summary>
@ -65,8 +58,11 @@ namespace MapControl
set { SetValue(FillRuleProperty, value); } set { SetValue(FillRuleProperty, value); }
} }
private void LocationsChanged(INotifyCollectionChanged oldCollection, INotifyCollectionChanged newCollection) private void LocationsPropertyChanged(DependencyPropertyChangedEventArgs e)
{ {
var oldCollection = e.OldValue as INotifyCollectionChanged;
var newCollection = e.NewValue as INotifyCollectionChanged;
if (oldCollection != null) if (oldCollection != null)
{ {
oldCollection.CollectionChanged -= LocationCollectionChanged; oldCollection.CollectionChanged -= LocationCollectionChanged;

176
MapControl/MapProjection.cs Normal file
View file

@ -0,0 +1,176 @@
// 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.Globalization;
#if NETFX_CORE
using Windows.Foundation;
using Windows.UI.Xaml.Media;
#else
using System.Windows;
using System.Windows.Media;
#endif
namespace MapControl
{
/// <summary>
/// Defines a map projection between geographic coordinates and cartesian map coordinates
/// and viewport coordinates, i.e. pixels.
/// </summary>
public abstract partial class MapProjection
{
public const double Wgs84EquatorialRadius = 6378137d;
public const double MetersPerDegree = Wgs84EquatorialRadius * Math.PI / 180d;
/// <summary>
/// Gets or sets the WMS 1.3.0 CRS Identifier.
/// </summary>
public string CrsId { get; set; }
/// <summary>
/// Indicates if this is a web mercator projection, i.e. compatible with map tile layers.
/// </summary>
public bool IsWebMercator { get; protected set; } = false;
/// <summary>
/// Indicates if this is an azimuthal projection.
/// </summary>
public bool IsAzimuthal { get; protected set; } = false;
/// <summary>
/// Gets the scale factor from longitude to x values of a normal cylindrical projection.
/// Returns NaN if this is not a normal cylindrical projection.
/// </summary>
public double LongitudeScale { get; protected set; } = 1d;
/// <summary>
/// Gets the absolute value of the minimum and maximum latitude that can be transformed.
/// </summary>
public double MaxLatitude { get; protected set; } = 90d;
/// <summary>
/// Gets the scaling factor from cartesian map coordinates to viewport coordinates.
/// </summary>
public double ViewportScale { get; protected set; }
/// <summary>
/// Gets the transformation from cartesian map coordinates to viewport coordinates (pixels).
/// </summary>
public MatrixTransform ViewportTransform { get; } = new MatrixTransform();
/// <summary>
/// Gets the scaling factor from cartesian map coordinates to viewport coordinates for the specified zoom level.
/// </summary>
public virtual double GetViewportScale(double zoomLevel)
{
return Math.Pow(2d, zoomLevel) * TileSource.TileSize / 360d;
}
/// <summary>
/// Gets the map scale at the specified Location as viewport coordinate units per meter (px/m).
/// </summary>
public abstract Point GetMapScale(Location location);
/// <summary>
/// Transforms a Location in geographic coordinates to a Point in cartesian map coordinates.
/// </summary>
public abstract Point LocationToPoint(Location location);
/// <summary>
/// Transforms a Point in cartesian map coordinates to a Location in geographic coordinates.
/// </summary>
public abstract Location PointToLocation(Point point);
/// <summary>
/// Translates a Location in geographic coordinates by the specified small amount in viewport coordinates.
/// </summary>
public abstract Location TranslateLocation(Location location, Point translation);
/// <summary>
/// Transforms a BoundingBox in geographic coordinates to a Rect in cartesian map coordinates.
/// </summary>
public virtual Rect BoundingBoxToRect(BoundingBox boundingBox)
{
return new Rect(
LocationToPoint(new Location(boundingBox.South, boundingBox.West)),
LocationToPoint(new Location(boundingBox.North, boundingBox.East)));
}
/// <summary>
/// Transforms a Rect in cartesian map coordinates to a BoundingBox in geographic coordinates.
/// </summary>
public virtual BoundingBox RectToBoundingBox(Rect rect)
{
var sw = PointToLocation(new Point(rect.X, rect.Y));
var ne = PointToLocation(new Point(rect.X + rect.Width, rect.Y + rect.Height));
return new BoundingBox(sw.Latitude, sw.Longitude, ne.Latitude, ne.Longitude);
}
/// <summary>
/// Transforms a Location in geographic coordinates to a Point in viewport coordinates.
/// </summary>
public Point LocationToViewportPoint(Location location)
{
return ViewportTransform.Transform(LocationToPoint(location));
}
/// <summary>
/// Transforms a Point in viewport coordinates to a Location in geographic coordinates.
/// </summary>
public Location ViewportPointToLocation(Point point)
{
return PointToLocation(ViewportTransform.Inverse.Transform(point));
}
/// <summary>
/// Transforms a Rect in viewport coordinates to a BoundingBox in geographic coordinates.
/// </summary>
public BoundingBox ViewportRectToBoundingBox(Rect rect)
{
return RectToBoundingBox(ViewportTransform.Inverse.TransformBounds(rect));
}
/// <summary>
/// Sets ViewportScale and ViewportTransform values.
/// </summary>
public virtual void SetViewportTransform(Location projectionCenter, Location mapCenter, Point viewportCenter, double zoomLevel, double heading)
{
ViewportScale = GetViewportScale(zoomLevel);
ViewportTransform.Matrix = MatrixEx.TranslateScaleRotateTranslate(
LocationToPoint(mapCenter), ViewportScale, -ViewportScale, heading, viewportCenter);
}
/// <summary>
/// Gets a WMS 1.3.0 query parameter string from the specified bounding box,
/// e.g. "CRS=...&BBOX=...&WIDTH=...&HEIGHT=..."
/// </summary>
public virtual string WmsQueryParameters(BoundingBox boundingBox, string version = "1.3.0")
{
if (string.IsNullOrEmpty(CrsId))
{
return null;
}
var format = "CRS={0}&BBOX={1},{2},{3},{4}&WIDTH={5}&HEIGHT={6}";
if (version.StartsWith("1.1."))
{
format = "SRS={0}&BBOX={1},{2},{3},{4}&WIDTH={5}&HEIGHT={6}";
}
else if (CrsId == "EPSG:4326")
{
format = "CRS={0}&BBOX={2},{1},{4},{3}&WIDTH={5}&HEIGHT={6}";
}
var rect = BoundingBoxToRect(boundingBox);
var width = (int)Math.Round(ViewportScale * rect.Width);
var height = (int)Math.Round(ViewportScale * rect.Height);
return string.Format(CultureInfo.InvariantCulture, format, CrsId,
rect.X, rect.Y, (rect.X + rect.Width), (rect.Y + rect.Height), width, height);
}
}
}

View file

@ -1,29 +0,0 @@
// XAML Map Control - http://xamlmapcontrol.codeplex.com/
// © 2016 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL)
using System.Windows;
using System.Windows.Media;
namespace MapControl
{
public partial class MapRectangle
{
static partial void ScaleRect(ref Rect rect, ref Transform transform)
{
// Scales the RectangleGeometry to compensate inaccurate hit testing in WPF.
// See http://stackoverflow.com/a/19335624/1136211
rect.Scale(1e6, 1e6);
var scaleTransform = new ScaleTransform(1e-6, 1e-6); // reverts rect scaling
scaleTransform.Freeze();
var transformGroup = new TransformGroup();
transformGroup.Children.Add(scaleTransform);
transformGroup.Children.Add(transform);
transform = transformGroup;
}
}
}

View file

@ -1,121 +0,0 @@
// XAML Map Control - http://xamlmapcontrol.codeplex.com/
// © 2016 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL)
#if NETFX_CORE
using Windows.Foundation;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Media;
#else
using System.Windows;
using System.Windows.Media;
#endif
namespace MapControl
{
/// <summary>
/// Fills a rectangular area defined by South, North, West and East with a Brush.
/// </summary>
public partial class MapRectangle : MapPath
{
public static readonly DependencyProperty WestProperty = DependencyProperty.Register(
"West", typeof(double), typeof(MapRectangle),
new PropertyMetadata(double.NaN, (o, e) => ((MapRectangle)o).UpdateData()));
public static readonly DependencyProperty EastProperty = DependencyProperty.Register(
"East", typeof(double), typeof(MapRectangle),
new PropertyMetadata(double.NaN, (o, e) => ((MapRectangle)o).UpdateData()));
public static readonly DependencyProperty SouthProperty = DependencyProperty.Register(
"South", typeof(double), typeof(MapRectangle),
new PropertyMetadata(double.NaN, (o, e) => ((MapRectangle)o).UpdateData()));
public static readonly DependencyProperty NorthProperty = DependencyProperty.Register(
"North", typeof(double), typeof(MapRectangle),
new PropertyMetadata(double.NaN, (o, e) => ((MapRectangle)o).UpdateData()));
private bool boundingBoxValid;
public MapRectangle()
{
StrokeThickness = 0d;
Data = new RectangleGeometry();
boundingBoxValid = true;
}
public double West
{
get { return (double)GetValue(WestProperty); }
set { SetValue(WestProperty, value); }
}
public double East
{
get { return (double)GetValue(EastProperty); }
set { SetValue(EastProperty, value); }
}
public double South
{
get { return (double)GetValue(SouthProperty); }
set { SetValue(SouthProperty, value); }
}
public double North
{
get { return (double)GetValue(NorthProperty); }
set { SetValue(NorthProperty, value); }
}
public bool BoundingBoxValid
{
get
{
return !double.IsNaN(South) && !double.IsNaN(North)
&& !double.IsNaN(West) && !double.IsNaN(East)
&& South < North && West < East;
}
}
public void SetBoundingBox(double west, double east, double south, double north)
{
if (West != west || East != east || South != south || North != north)
{
boundingBoxValid = false;
West = west;
East = east;
South = south;
North = north;
boundingBoxValid = true;
UpdateData();
}
}
protected override void UpdateData()
{
if (boundingBoxValid)
{
var geometry = (RectangleGeometry)Data;
if (ParentMap != null && BoundingBoxValid)
{
var rect = new Rect(ParentMap.MapTransform.Transform(new Location(South, West)),
ParentMap.MapTransform.Transform(new Location(North, East)));
Transform transform = ViewportTransform;
ScaleRect(ref rect, ref transform);
geometry.Rect = rect;
RenderTransform = transform;
}
else
{
geometry.ClearValue(RectangleGeometry.RectProperty);
ClearValue(RenderTransformProperty);
}
}
}
static partial void ScaleRect(ref Rect rect, ref Transform transform); // WPF only
}
}

View file

@ -1,5 +1,5 @@
// XAML Map Control - http://xamlmapcontrol.codeplex.com/ // XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2016 Clemens Fischer // © 2017 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL) // Licensed under the Microsoft Public License (Ms-PL)
using System; using System;
@ -23,23 +23,12 @@ namespace MapControl
static MapScale() static MapScale()
{ {
IsHitTestVisibleProperty.OverrideMetadata( IsHitTestVisibleProperty.OverrideMetadata(typeof(MapScale), new FrameworkPropertyMetadata(false));
typeof(MapScale), new FrameworkPropertyMetadata(false)); MinWidthProperty.OverrideMetadata(typeof(MapScale), new FrameworkPropertyMetadata(100d));
HorizontalAlignmentProperty.OverrideMetadata(typeof(MapScale), new FrameworkPropertyMetadata(HorizontalAlignment.Right));
MinWidthProperty.OverrideMetadata( VerticalAlignmentProperty.OverrideMetadata(typeof(MapScale), new FrameworkPropertyMetadata(VerticalAlignment.Bottom));
typeof(MapScale), new FrameworkPropertyMetadata(100d)); StrokeStartLineCapProperty.OverrideMetadata(typeof(MapScale), new FrameworkPropertyMetadata(PenLineCap.Round));
StrokeEndLineCapProperty.OverrideMetadata(typeof(MapScale), new FrameworkPropertyMetadata(PenLineCap.Round));
HorizontalAlignmentProperty.OverrideMetadata(
typeof(MapScale), new FrameworkPropertyMetadata(HorizontalAlignment.Right));
VerticalAlignmentProperty.OverrideMetadata(
typeof(MapScale), new FrameworkPropertyMetadata(VerticalAlignment.Bottom));
StrokeStartLineCapProperty.OverrideMetadata(
typeof(MapScale), new FrameworkPropertyMetadata(PenLineCap.Round));
StrokeEndLineCapProperty.OverrideMetadata(
typeof(MapScale), new FrameworkPropertyMetadata(PenLineCap.Round));
} }
public Thickness Padding public Thickness Padding
@ -50,9 +39,9 @@ namespace MapControl
protected override Size MeasureOverride(Size availableSize) protected override Size MeasureOverride(Size availableSize)
{ {
if (ParentMap != null && ParentMap.CenterScale > 0d) if (ParentMap != null && ParentMap.ScaleTransform.ScaleX > 0d)
{ {
length = MinWidth / ParentMap.CenterScale; length = MinWidth / ParentMap.ScaleTransform.ScaleX;
var magnitude = Math.Pow(10d, Math.Floor(Math.Log10(length))); var magnitude = Math.Pow(10d, Math.Floor(Math.Log10(length)));
if (length / magnitude < 2d) if (length / magnitude < 2d)
@ -68,7 +57,7 @@ namespace MapControl
length = 10d * magnitude; length = 10d * magnitude;
} }
size.Width = length * ParentMap.CenterScale + StrokeThickness + Padding.Left + Padding.Right; size.Width = length * ParentMap.ScaleTransform.ScaleX + StrokeThickness + Padding.Left + Padding.Right;
size.Height = FontSize * FontFamily.LineSpacing + StrokeThickness + Padding.Top + Padding.Bottom; size.Height = FontSize * FontFamily.LineSpacing + StrokeThickness + Padding.Top + Padding.Bottom;
} }
else else

View file

@ -0,0 +1,16 @@
// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2017 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL)
namespace MapControl
{
public partial class MapTileLayer
{
partial void Initialize()
{
IsHitTestVisible = false;
MapPanel.AddParentMapHandlers(this);
}
}
}

View file

@ -0,0 +1,16 @@
// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2017 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL)
using System.Windows;
namespace MapControl
{
public partial class MapTileLayer
{
static MapTileLayer()
{
IsHitTestVisibleProperty.OverrideMetadata(typeof(MapTileLayer), new FrameworkPropertyMetadata(false));
}
}
}

View file

@ -1,5 +1,5 @@
// XAML Map Control - http://xamlmapcontrol.codeplex.com/ // XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2016 Clemens Fischer // © 2017 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL) // Licensed under the Microsoft Public License (Ms-PL)
using System; using System;
@ -8,10 +8,12 @@ using System.Linq;
#if NETFX_CORE #if NETFX_CORE
using Windows.Foundation; using Windows.Foundation;
using Windows.UI.Xaml; using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Markup; using Windows.UI.Xaml.Markup;
using Windows.UI.Xaml.Media; using Windows.UI.Xaml.Media;
#else #else
using System.Windows; using System.Windows;
using System.Windows.Controls;
using System.Windows.Markup; using System.Windows.Markup;
using System.Windows.Media; using System.Windows.Media;
using System.Windows.Threading; using System.Windows.Threading;
@ -19,6 +21,12 @@ using System.Windows.Threading;
namespace MapControl namespace MapControl
{ {
public interface ITileImageLoader
{
void BeginLoadTiles(MapTileLayer tileLayer, IEnumerable<Tile> tiles);
void CancelLoadTiles(MapTileLayer tileLayer);
}
/// <summary> /// <summary>
/// Fills the map viewport with map tiles from a TileSource. /// Fills the map viewport with map tiles from a TileSource.
/// </summary> /// </summary>
@ -27,13 +35,16 @@ namespace MapControl
#else #else
[ContentProperty("TileSource")] [ContentProperty("TileSource")]
#endif #endif
public partial class TileLayer : PanelBase, IMapElement public partial class MapTileLayer : Panel, IMapLayer
{ {
public static TileLayer OpenStreetMapTileLayer /// <summary>
/// A default TileLayer using OpenStreetMap data.
/// </summary>
public static MapTileLayer OpenStreetMapTileLayer
{ {
get get
{ {
return new TileLayer return new MapTileLayer
{ {
SourceName = "OpenStreetMap", SourceName = "OpenStreetMap",
Description = "© [OpenStreetMap Contributors](http://www.openstreetmap.org/copyright)", Description = "© [OpenStreetMap Contributors](http://www.openstreetmap.org/copyright)",
@ -44,56 +55,53 @@ namespace MapControl
} }
public static readonly DependencyProperty TileSourceProperty = DependencyProperty.Register( public static readonly DependencyProperty TileSourceProperty = DependencyProperty.Register(
"TileSource", typeof(TileSource), typeof(TileLayer), nameof(TileSource), typeof(TileSource), typeof(MapTileLayer),
new PropertyMetadata(null, (o, e) => ((TileLayer)o).UpdateTiles(true))); new PropertyMetadata(null, (o, e) => ((MapTileLayer)o).UpdateTiles(true)));
public static readonly DependencyProperty SourceNameProperty = DependencyProperty.Register( public static readonly DependencyProperty SourceNameProperty = DependencyProperty.Register(
"SourceName", typeof(string), typeof(TileLayer), new PropertyMetadata(null)); nameof(SourceName), typeof(string), typeof(MapTileLayer), new PropertyMetadata(null));
public static readonly DependencyProperty DescriptionProperty = DependencyProperty.Register( public static readonly DependencyProperty DescriptionProperty = DependencyProperty.Register(
"Description", typeof(string), typeof(TileLayer), new PropertyMetadata(null)); nameof(Description), typeof(string), typeof(MapTileLayer), new PropertyMetadata(null));
public static readonly DependencyProperty LogoImageProperty = DependencyProperty.Register(
"LogoImage", typeof(ImageSource), typeof(TileLayer), new PropertyMetadata(null));
public static readonly DependencyProperty ZoomLevelOffsetProperty = DependencyProperty.Register( public static readonly DependencyProperty ZoomLevelOffsetProperty = DependencyProperty.Register(
"ZoomLevelOffset", typeof(double), typeof(TileLayer), nameof(ZoomLevelOffset), typeof(double), typeof(MapTileLayer),
new PropertyMetadata(0d, (o, e) => ((TileLayer)o).UpdateTileGrid())); new PropertyMetadata(0d, (o, e) => ((MapTileLayer)o).UpdateTileGrid()));
public static readonly DependencyProperty MinZoomLevelProperty = DependencyProperty.Register( public static readonly DependencyProperty MinZoomLevelProperty = DependencyProperty.Register(
"MinZoomLevel", typeof(int), typeof(TileLayer), new PropertyMetadata(0)); nameof(MinZoomLevel), typeof(int), typeof(MapTileLayer), new PropertyMetadata(0));
public static readonly DependencyProperty MaxZoomLevelProperty = DependencyProperty.Register( public static readonly DependencyProperty MaxZoomLevelProperty = DependencyProperty.Register(
"MaxZoomLevel", typeof(int), typeof(TileLayer), new PropertyMetadata(18)); nameof(MaxZoomLevel), typeof(int), typeof(MapTileLayer), new PropertyMetadata(18));
public static readonly DependencyProperty MaxParallelDownloadsProperty = DependencyProperty.Register( public static readonly DependencyProperty MaxParallelDownloadsProperty = DependencyProperty.Register(
"MaxParallelDownloads", typeof(int), typeof(TileLayer), new PropertyMetadata(4)); nameof(MaxParallelDownloads), typeof(int), typeof(MapTileLayer), new PropertyMetadata(4));
public static readonly DependencyProperty UpdateIntervalProperty = DependencyProperty.Register( public static readonly DependencyProperty UpdateIntervalProperty = DependencyProperty.Register(
"UpdateInterval", typeof(TimeSpan), typeof(TileLayer), nameof(UpdateInterval), typeof(TimeSpan), typeof(MapTileLayer),
new PropertyMetadata(TimeSpan.FromSeconds(0.5), (o, e) => ((TileLayer)o).updateTimer.Interval = (TimeSpan)e.NewValue)); new PropertyMetadata(TimeSpan.FromSeconds(0.2), (o, e) => ((MapTileLayer)o).updateTimer.Interval = (TimeSpan)e.NewValue));
public static readonly DependencyProperty UpdateWhileViewportChangingProperty = DependencyProperty.Register( public static readonly DependencyProperty UpdateWhileViewportChangingProperty = DependencyProperty.Register(
"UpdateWhileViewportChanging", typeof(bool), typeof(TileLayer), new PropertyMetadata(true)); nameof(UpdateWhileViewportChanging), typeof(bool), typeof(MapTileLayer), new PropertyMetadata(true));
public static readonly DependencyProperty LoadTilesDescendingProperty = DependencyProperty.Register( public static readonly DependencyProperty LoadTilesDescendingProperty = DependencyProperty.Register(
"LoadTilesDescending", typeof(bool), typeof(TileLayer), new PropertyMetadata(false)); nameof(LoadTilesDescending), typeof(bool), typeof(MapTileLayer), new PropertyMetadata(false));
public static readonly DependencyProperty ForegroundProperty = DependencyProperty.Register( public static readonly DependencyProperty MapBackgroundProperty = DependencyProperty.Register(
"Foreground", typeof(Brush), typeof(TileLayer), new PropertyMetadata(null)); nameof(MapBackground), typeof(Brush), typeof(MapTileLayer), new PropertyMetadata(null));
public static readonly new DependencyProperty BackgroundProperty = DependencyProperty.Register( public static readonly DependencyProperty MapForegroundProperty = DependencyProperty.Register(
"Background", typeof(Brush), typeof(TileLayer), new PropertyMetadata(null)); nameof(MapForeground), typeof(Brush), typeof(MapTileLayer), new PropertyMetadata(null));
private readonly DispatcherTimer updateTimer; private readonly DispatcherTimer updateTimer;
private MapBase parentMap; private MapBase parentMap;
public TileLayer() public MapTileLayer()
: this(new TileImageLoader()) : this(new TileImageLoader())
{ {
} }
public TileLayer(ITileImageLoader tileImageLoader) public MapTileLayer(ITileImageLoader tileImageLoader)
{ {
Initialize(); Initialize();
@ -130,7 +138,8 @@ namespace MapControl
} }
/// <summary> /// <summary>
/// Description of the TileLayer. Used to display copyright information on top of the map. /// Description of the TileLayer.
/// Used to display copyright information on top of the map.
/// </summary> /// </summary>
public string Description public string Description
{ {
@ -138,15 +147,6 @@ namespace MapControl
set { SetValue(DescriptionProperty, value); } set { SetValue(DescriptionProperty, value); }
} }
/// <summary>
/// Logo image. Used to display a provider brand logo on top of the map.
/// </summary>
public ImageSource LogoImage
{
get { return (ImageSource)GetValue(LogoImageProperty); }
set { SetValue(LogoImageProperty, value); }
}
/// <summary> /// <summary>
/// Adds an offset to the Map's ZoomLevel for a relative scale between the Map and the TileLayer. /// Adds an offset to the Map's ZoomLevel for a relative scale between the Map and the TileLayer.
/// </summary> /// </summary>
@ -193,7 +193,7 @@ namespace MapControl
} }
/// <summary> /// <summary>
/// Controls if tiles are updates while the viewport is still changing. /// Controls if tiles are updated while the viewport is still changing.
/// </summary> /// </summary>
public bool UpdateWhileViewportChanging public bool UpdateWhileViewportChanging
{ {
@ -212,22 +212,23 @@ namespace MapControl
} }
/// <summary> /// <summary>
/// Optional foreground brush. Sets MapBase.Foreground, if not null. /// Optional background brush.
/// Sets MapBase.Background if not null and the TileLayer is the base map layer.
/// </summary> /// </summary>
public Brush Foreground public Brush MapBackground
{ {
get { return (Brush)GetValue(ForegroundProperty); } get { return (Brush)GetValue(MapBackgroundProperty); }
set { SetValue(ForegroundProperty, value); } set { SetValue(MapBackgroundProperty, value); }
} }
/// <summary> /// <summary>
/// Optional background brush. Sets MapBase.Background, if not null. /// Optional foreground brush.
/// New property prevents filling of RenderTransformed TileLayer with Panel.Background. /// Sets MapBase.Foreground if not null and the TileLayer is the base map layer.
/// </summary> /// </summary>
public new Brush Background public Brush MapForeground
{ {
get { return (Brush)GetValue(BackgroundProperty); } get { return (Brush)GetValue(MapForegroundProperty); }
set { SetValue(BackgroundProperty, value); } set { SetValue(MapForegroundProperty, value); }
} }
public MapBase ParentMap public MapBase ParentMap
@ -251,11 +252,64 @@ namespace MapControl
} }
} }
protected override Size MeasureOverride(Size availableSize)
{
availableSize = new Size(double.PositiveInfinity, double.PositiveInfinity);
foreach (UIElement element in Children)
{
element.Measure(availableSize);
}
return new Size();
}
protected override Size ArrangeOverride(Size finalSize)
{
if (TileGrid != null)
{
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;
tile.Image.Width = tileSize;
tile.Image.Height = tileSize;
tile.Image.Arrange(new Rect(x, y, tileSize, tileSize));
}
}
return finalSize;
}
protected virtual void UpdateTileGrid()
{
updateTimer.Stop();
if (parentMap != null && parentMap.MapProjection.IsWebMercator)
{
var tileGrid = GetTileGrid();
if (!tileGrid.Equals(TileGrid))
{
TileGrid = tileGrid;
SetRenderTransform();
UpdateTiles(false);
}
}
else
{
TileGrid = null;
UpdateTiles(true);
}
}
private void OnViewportChanged(object sender, ViewportChangedEventArgs e) private void OnViewportChanged(object sender, ViewportChangedEventArgs e)
{ {
if (TileGrid == null || Math.Abs(e.OriginOffset) > 180d) if (TileGrid == null || e.ProjectionChanged || Math.Abs(e.LongitudeOffset) > 180d)
{ {
// immediate update when map center moves across 180° longitude // update immediately when map projection has changed or map center has moved across 180° longitude
UpdateTileGrid(); UpdateTileGrid();
} }
else else
@ -274,33 +328,47 @@ namespace MapControl
} }
} }
protected void UpdateTileGrid() private Point GetTileCenter(double tileScale)
{ {
updateTimer.Stop(); // map center in tile index coordinates
return new Point(
if (parentMap != null) tileScale * (0.5 + parentMap.Center.Longitude / 360d),
{ tileScale * (0.5 - WebMercatorProjection.LatitudeToY(parentMap.Center.Latitude) / 360d));
var zoomLevel = Math.Max(0, (int)Math.Round(parentMap.ZoomLevel + ZoomLevelOffset));
var bounds = GetTileIndexBounds(zoomLevel);
var tileGrid = new TileGrid(zoomLevel,
(int)Math.Floor(bounds.X), (int)Math.Floor(bounds.Y),
(int)Math.Floor(bounds.X + bounds.Width), (int)Math.Floor(bounds.Y + bounds.Height));
if (!tileGrid.Equals(TileGrid))
{
TileGrid = tileGrid;
SetRenderTransform();
UpdateTiles(false);
}
}
else
{
TileGrid = null;
UpdateTiles(true);
}
} }
protected virtual void UpdateTiles(bool clearTiles) 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 tileCenter = GetTileCenter(tileScale);
var viewCenter = new Point(parentMap.RenderSize.Width / 2d, parentMap.RenderSize.Height / 2d);
var transform = new MatrixTransform
{
Matrix = MatrixEx.TranslateScaleRotateTranslate(viewCenter, scale, -parentMap.Heading, tileCenter)
};
var bounds = transform.TransformBounds(new Rect(0d, 0d, parentMap.RenderSize.Width, parentMap.RenderSize.Height));
return new TileGrid(tileZoomLevel,
(int)Math.Floor(bounds.X), (int)Math.Floor(bounds.Y),
(int)Math.Floor(bounds.X + bounds.Width), (int)Math.Floor(bounds.Y + bounds.Height));
}
private void SetRenderTransform()
{
var tileScale = (1 << TileGrid.ZoomLevel);
var scale = Math.Pow(2d, parentMap.ZoomLevel) / tileScale;
var tileCenter = GetTileCenter(tileScale);
var tileOrigin = new Point(TileSource.TileSize * (tileCenter.X - TileGrid.XMin), TileSource.TileSize * (tileCenter.Y - TileGrid.YMin));
var viewCenter = new Point(parentMap.RenderSize.Width / 2d, parentMap.RenderSize.Height / 2d);
((MatrixTransform)RenderTransform).Matrix =
MatrixEx.TranslateScaleRotateTranslate(tileOrigin, scale, parentMap.Heading, viewCenter);
}
private void UpdateTiles(bool clearTiles)
{ {
if (Tiles.Count > 0) if (Tiles.Count > 0)
{ {
@ -334,16 +402,16 @@ namespace MapControl
} }
} }
protected void SelectTiles() private void SelectTiles()
{ {
var newTiles = new List<Tile>(); var newTiles = new List<Tile>();
if (TileGrid != null && parentMap != null && TileSource != null) if (parentMap != null && TileGrid != null && TileSource != null)
{ {
var maxZoomLevel = Math.Min(TileGrid.ZoomLevel, MaxZoomLevel); var maxZoomLevel = Math.Min(TileGrid.ZoomLevel, MaxZoomLevel);
var minZoomLevel = MinZoomLevel; var minZoomLevel = MinZoomLevel;
if (minZoomLevel < maxZoomLevel && this != parentMap.TileLayers.FirstOrDefault()) if (minZoomLevel < maxZoomLevel && this != parentMap.Children.Cast<UIElement>().FirstOrDefault())
{ {
// do not load background tiles if this is not the base layer // do not load background tiles if this is not the base layer
minZoomLevel = maxZoomLevel; minZoomLevel = maxZoomLevel;
@ -385,24 +453,5 @@ namespace MapControl
Tiles = newTiles; Tiles = newTiles;
} }
protected override Size ArrangeOverride(Size finalSize)
{
if (TileGrid != null)
{
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;
tile.Image.Width = tileSize;
tile.Image.Height = tileSize;
tile.Image.Arrange(new Rect(x, y, tileSize, tileSize));
}
}
return finalSize;
}
} }
} }

View file

@ -1,61 +0,0 @@
// XAML Map Control - http://xamlmapcontrol.codeplex.com/
// © 2016 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL)
#if NETFX_CORE
using System;
using Windows.Foundation;
#else
using System.Windows;
#endif
namespace MapControl
{
/// <summary>
/// Defines a normal cylindrical projection. Latitude and longitude values in degrees are
/// transformed to cartesian coordinates with origin at latitude = 0 and longitude = 0.
/// Longitude values are transformed identically to x values in the interval [-180 .. 180].
/// Latitude values in the interval [-MaxLatitude .. MaxLatitude] are transformed to y values in
/// the interval [-180 .. 180] according to the actual projection, e.g. the Mercator projection.
/// </summary>
public abstract class MapTransform
{
/// <summary>
/// Gets the absolute value of the minimum and maximum latitude that can be transformed.
/// </summary>
public abstract double MaxLatitude { get; }
/// <summary>
/// Gets the scale factor of the map projection at the specified
/// geographic location relative to the scale at latitude zero.
/// </summary>
public abstract double RelativeScale(Location location);
/// <summary>
/// Transforms a geographic location to a cartesian coordinate point.
/// </summary>
public abstract Point Transform(Location location);
/// <summary>
/// Transforms a cartesian coordinate point to a geographic location.
/// </summary>
public abstract Location Transform(Point point);
/// <summary>
/// Transforms a geographic location by the specified translation in viewport coordinates.
/// </summary>
public Location Transform(Location origin, Point mapOrigin, Point translation)
{
#if NETFX_CORE
var latitudeTranslation = translation.Y / RelativeScale(origin);
if (Math.Abs(latitudeTranslation) < 1e-3) // avoid rounding errors
{
return new Location(origin.Latitude + latitudeTranslation, origin.Longitude + translation.X);
}
#endif
return Transform(new Point(mapOrigin.X + translation.X, mapOrigin.Y + translation.Y));
}
}
}

View file

@ -1,11 +1,13 @@
// XAML Map Control - http://xamlmapcontrol.codeplex.com/ // XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2016 Clemens Fischer // © 2017 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL) // Licensed under the Microsoft Public License (Ms-PL)
using System; using System;
#if NETFX_CORE #if NETFX_CORE
using Windows.Foundation;
using Windows.UI.Xaml.Media; using Windows.UI.Xaml.Media;
#else #else
using System.Windows;
using System.Windows.Media; using System.Windows.Media;
#endif #endif
@ -13,25 +15,43 @@ namespace MapControl
{ {
internal static class MatrixEx internal static class MatrixEx
{ {
public static Matrix Translate(this Matrix matrix, double offsetX, double offsetY) /// <summary>
/// Used in MapProjection.
/// </summary>
public static Matrix TranslateScaleRotateTranslate(
Point translation1, double scaleX, double scaleY, double rotationAngle, Point translation2)
{
return new Matrix(1d, 0d, 0d, 1d, -translation1.X, -translation1.Y)
.Scale(scaleX, scaleY)
.Rotate(rotationAngle)
.Translate(translation2.X, translation2.Y);
}
/// <summary>
/// Used in TileLayer.
/// </summary>
public static Matrix TranslateScaleRotateTranslate(
Point translation1, double scale, double rotationAngle, Point translation2)
{
return new Matrix(1d, 0d, 0d, 1d, -translation1.X, -translation1.Y)
.Scale(scale, scale)
.Rotate(rotationAngle)
.Translate(translation2.X, translation2.Y);
}
private static Matrix Translate(this Matrix matrix, double offsetX, double offsetY)
{ {
matrix.OffsetX += offsetX; matrix.OffsetX += offsetX;
matrix.OffsetY += offsetY; matrix.OffsetY += offsetY;
return matrix; return matrix;
} }
public static Matrix TranslatePrepend(this Matrix matrix, double offsetX, double offsetY) private static Matrix Scale(this Matrix matrix, double scaleX, double scaleY)
{
return new Matrix(1d, 0d, 0d, 1d, offsetX, offsetY).Multiply(matrix);
}
public static Matrix Scale(this Matrix matrix, double scaleX, double scaleY)
{ {
return Multiply(matrix, new Matrix(scaleX, 0d, 0d, scaleY, 0d, 0d)); return Multiply(matrix, new Matrix(scaleX, 0d, 0d, scaleY, 0d, 0d));
} }
public static Matrix Rotate(this Matrix matrix, double angle) private static Matrix Rotate(this Matrix matrix, double angle)
{ {
if (angle == 0d) if (angle == 0d)
{ {
@ -45,23 +65,7 @@ namespace MapControl
return Multiply(matrix, new Matrix(cos, sin, -sin, cos, 0d, 0d)); return Multiply(matrix, new Matrix(cos, sin, -sin, cos, 0d, 0d));
} }
public static Matrix RotateAt(this Matrix matrix, double angle, double centerX, double centerY) private static Matrix Invert(this Matrix matrix)
{
if (angle == 0d)
{
return matrix;
}
angle = (angle % 360d) / 180d * Math.PI;
var cos = Math.Cos(angle);
var sin = Math.Sin(angle);
var offsetX = centerX * (1d - cos) + centerY * sin;
var offsetY = centerY * (1d - cos) - centerX * sin;
return Multiply(matrix, new Matrix(cos, sin, -sin, cos, offsetX, offsetY));
}
public static Matrix Invert(this Matrix matrix)
{ {
var determinant = matrix.M11 * matrix.M22 - matrix.M12 * matrix.M21; var determinant = matrix.M11 * matrix.M22 - matrix.M12 * matrix.M21;
@ -74,7 +78,7 @@ namespace MapControl
(matrix.M12 * matrix.OffsetX - matrix.M11 * matrix.OffsetY) / determinant); (matrix.M12 * matrix.OffsetX - matrix.M11 * matrix.OffsetY) / determinant);
} }
public static Matrix Multiply(this Matrix matrix1, Matrix matrix2) private static Matrix Multiply(this Matrix matrix1, Matrix matrix2)
{ {
return new Matrix( return new Matrix(
matrix1.M11 * matrix2.M11 + matrix1.M12 * matrix2.M21, matrix1.M11 * matrix2.M11 + matrix1.M12 * matrix2.M21,

View file

@ -0,0 +1,38 @@
// 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;
namespace MapControl
{
internal static class MatrixEx
{
/// <summary>
/// Used in MapProjection.
/// </summary>
public static Matrix TranslateScaleRotateTranslate(
Point translation1, double scaleX, double scaleY, double rotationAngle, Point translation2)
{
var matrix = new Matrix(1d, 0d, 0d, 1d, -translation1.X, -translation1.Y);
matrix.Scale(scaleX, scaleY);
matrix.Rotate(rotationAngle);
matrix.Translate(translation2.X, translation2.Y);
return matrix;
}
/// <summary>
/// Used in TileLayer.
/// </summary>
public static Matrix TranslateScaleRotateTranslate(
Point translation1, double scale, double rotationAngle, Point translation2)
{
var matrix = new Matrix(1d, 0d, 0d, 1d, -translation1.X, -translation1.Y);
matrix.Scale(scale, scale);
matrix.Rotate(rotationAngle);
matrix.Translate(translation2.X, translation2.Y);
return matrix;
}
}
}

View file

@ -1,77 +0,0 @@
// XAML Map Control - http://xamlmapcontrol.codeplex.com/
// © 2016 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL)
using System;
#if NETFX_CORE
using Windows.Foundation;
#else
using System.Windows;
#endif
namespace MapControl
{
/// <summary>
/// Transforms latitude and longitude values in degrees to cartesian coordinates
/// according to the Mercator projection.
/// </summary>
public class MercatorTransform : MapTransform
{
public static readonly double MaxLatitudeValue = YToLatitude(180d);
public static double RelativeScale(double latitude)
{
if (latitude <= -90d)
{
return double.NegativeInfinity;
}
if (latitude >= 90d)
{
return double.PositiveInfinity;
}
return 1d / Math.Cos(latitude * Math.PI / 180d);
}
public static double LatitudeToY(double latitude)
{
if (latitude <= -90d)
{
return double.NegativeInfinity;
}
if (latitude >= 90d)
{
return double.PositiveInfinity;
}
return Math.Log(Math.Tan((latitude + 90d) * Math.PI / 360d)) / Math.PI * 180d;
}
public static double YToLatitude(double y)
{
return Math.Atan(Math.Exp(y * Math.PI / 180d)) / Math.PI * 360d - 90d;
}
public override double MaxLatitude
{
get { return MaxLatitudeValue; }
}
public override double RelativeScale(Location location)
{
return RelativeScale(location.Latitude);
}
public override Point Transform(Location location)
{
return new Point(location.Longitude, LatitudeToY(location.Latitude));
}
public override Location Transform(Point point)
{
return new Location(YToLatitude(point.Y), point.X);
}
}
}

View file

@ -0,0 +1,74 @@
// 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;
#else
using System.Windows;
#endif
namespace MapControl
{
/// <summary>
/// Transforms map coordinates according to the Orthographic Projection.
/// </summary>
public class OrthographicProjection : AzimuthalProjection
{
public OrthographicProjection()
: this("AUTO2:42003")
{
}
public OrthographicProjection(string crsId)
{
CrsId = crsId;
}
public override Point LocationToPoint(Location location)
{
if (location.Equals(projectionCenter))
{
return new Point();
}
var lat0 = projectionCenter.Latitude * Math.PI / 180d;
var lat = location.Latitude * Math.PI / 180d;
var dLon = (location.Longitude - projectionCenter.Longitude) * Math.PI / 180d;
return new Point(
Wgs84EquatorialRadius * Math.Cos(lat) * Math.Sin(dLon),
Wgs84EquatorialRadius * (Math.Cos(lat0) * Math.Sin(lat) - Math.Sin(lat0) * Math.Cos(lat) * Math.Cos(dLon)));
}
public override Location PointToLocation(Point point)
{
if (point.X == 0d && point.Y == 0d)
{
return projectionCenter;
}
var x = point.X / Wgs84EquatorialRadius;
var y = point.Y / Wgs84EquatorialRadius;
var r2 = x * x + y * y;
if (r2 > 1d)
{
return new Location(double.NaN, double.NaN);
}
var r = Math.Sqrt(r2);
var sinC = r;
var cosC = Math.Sqrt(1 - r2);
var lat0 = projectionCenter.Latitude * Math.PI / 180d;
var cosLat0 = Math.Cos(lat0);
var sinLat0 = Math.Sin(lat0);
return new Location(
180d / Math.PI * Math.Asin(cosC * sinLat0 + y * sinC * cosLat0 / r),
180d / Math.PI * Math.Atan2(x * sinC, r * cosC * cosLat0 - y * sinC * sinLat0) + projectionCenter.Longitude);
}
}
}

View file

@ -1,43 +0,0 @@
// XAML Map Control - http://xamlmapcontrol.codeplex.com/
// © 2016 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL)
#if NETFX_CORE
using Windows.Foundation;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
#else
using System.Windows;
using System.Windows.Controls;
#endif
namespace MapControl
{
/// <summary>
/// Common base class for MapPanel and TileLayer.
/// </summary>
public class PanelBase : Panel
{
protected override Size MeasureOverride(Size availableSize)
{
availableSize = new Size(double.PositiveInfinity, double.PositiveInfinity);
foreach (UIElement element in Children)
{
element.Measure(availableSize);
}
return new Size();
}
protected override Size ArrangeOverride(Size finalSize)
{
foreach (UIElement element in Children)
{
element.Arrange(new Rect(new Point(), finalSize));
}
return finalSize;
}
}
}

View file

@ -12,10 +12,10 @@ using System.Windows;
#endif #endif
[assembly: AssemblyProduct("XAML Map Control")] [assembly: AssemblyProduct("XAML Map Control")]
[assembly: AssemblyCompany("Clemens Fischer")] [assembly: AssemblyCompany("Clemens Fischer")]
[assembly: AssemblyCopyright("© 2016 Clemens Fischer")] [assembly: AssemblyCopyright("© 2017 Clemens Fischer")]
[assembly: AssemblyTrademark("")] [assembly: AssemblyTrademark("")]
[assembly: AssemblyVersion("2.14.0")] [assembly: AssemblyVersion("3.1.0")]
[assembly: AssemblyFileVersion("2.14.0")] [assembly: AssemblyFileVersion("3.1.0")]
[assembly: AssemblyConfiguration("")] [assembly: AssemblyConfiguration("")]
[assembly: AssemblyCulture("")] [assembly: AssemblyCulture("")]
[assembly: ComVisible(false)] [assembly: ComVisible(false)]

View file

@ -1,5 +1,5 @@
// XAML Map Control - http://xamlmapcontrol.codeplex.com/ // XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2016 Clemens Fischer // © 2017 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL) // Licensed under the Microsoft Public License (Ms-PL)
#if NETFX_CORE #if NETFX_CORE

View file

@ -1,5 +1,5 @@
// XAML Map Control - http://xamlmapcontrol.codeplex.com/ // XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2016 Clemens Fischer // © 2017 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL) // Licensed under the Microsoft Public License (Ms-PL)
using System.Windows; using System.Windows;
@ -14,8 +14,7 @@ namespace MapControl
{ {
static Pushpin() static Pushpin()
{ {
DefaultStyleKeyProperty.OverrideMetadata( DefaultStyleKeyProperty.OverrideMetadata(typeof(Pushpin), new FrameworkPropertyMetadata(typeof(Pushpin)));
typeof(Pushpin), new FrameworkPropertyMetadata(typeof(Pushpin)));
} }
public static readonly DependencyProperty LocationProperty = public static readonly DependencyProperty LocationProperty =

View file

@ -0,0 +1,59 @@
// 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;
#else
using System.Windows;
#endif
namespace MapControl
{
/// <summary>
/// Transforms map coordinates according to the Gnomonic Projection.
/// </summary>
public class StereographicProjection : AzimuthalProjection
{
public StereographicProjection()
: this("AUTO2:97002") // GeoServer non-standard CRS ID
{
}
public StereographicProjection(string crsId)
{
CrsId = crsId;
}
public override Point LocationToPoint(Location location)
{
if (location.Equals(projectionCenter))
{
return new Point();
}
double azimuth, distance;
GetAzimuthDistance(projectionCenter, location, out azimuth, out distance);
var mapDistance = 2d * Wgs84EquatorialRadius * Math.Tan(distance / 2d);
return new Point(mapDistance * Math.Sin(azimuth), mapDistance * Math.Cos(azimuth));
}
public override Location PointToLocation(Point point)
{
if (point.X == 0d && point.Y == 0d)
{
return projectionCenter;
}
var azimuth = Math.Atan2(point.X, point.Y);
var mapDistance = Math.Sqrt(point.X * point.X + point.Y * point.Y);
var distance = 2d * Math.Atan(mapDistance / (2d * Wgs84EquatorialRadius));
return GetLocation(projectionCenter, azimuth, distance);
}
}
}

View file

@ -1,5 +1,5 @@
// XAML Map Control - http://xamlmapcontrol.codeplex.com/ // XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2016 Clemens Fischer // © 2017 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL) // Licensed under the Microsoft Public License (Ms-PL)
using System; using System;

View file

@ -1,5 +1,5 @@
// XAML Map Control - http://xamlmapcontrol.codeplex.com/ // XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2016 Clemens Fischer // © 2017 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL) // Licensed under the Microsoft Public License (Ms-PL)
using System; using System;

View file

@ -1,5 +1,5 @@
// XAML Map Control - http://xamlmapcontrol.codeplex.com/ // XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2016 Clemens Fischer // © 2017 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL) // Licensed under the Microsoft Public License (Ms-PL)
using System; using System;
@ -13,12 +13,7 @@ namespace MapControl
{ {
public partial class Tile public partial class Tile
{ {
public static TimeSpan FadeDuration { get; set; } public static TimeSpan FadeDuration { get; set; } = TimeSpan.FromSeconds(0.2);
static Tile()
{
FadeDuration = TimeSpan.FromSeconds(0.2);
}
public readonly int ZoomLevel; public readonly int ZoomLevel;
public readonly int X; public readonly int X;

View file

@ -1,5 +1,5 @@
// XAML Map Control - http://xamlmapcontrol.codeplex.com/ // XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2016 Clemens Fischer // © 2017 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL) // Licensed under the Microsoft Public License (Ms-PL)
using System; using System;

View file

@ -1,5 +1,5 @@
// XAML Map Control - http://xamlmapcontrol.codeplex.com/ // XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2016 Clemens Fischer // © 2017 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL) // Licensed under the Microsoft Public License (Ms-PL)
using System; using System;
@ -15,7 +15,7 @@ namespace MapControl
/// </summary> /// </summary>
internal class TileImageLoader : ITileImageLoader internal class TileImageLoader : ITileImageLoader
{ {
public void BeginLoadTiles(TileLayer tileLayer, IEnumerable<Tile> tiles) public void BeginLoadTiles(MapTileLayer tileLayer, IEnumerable<Tile> tiles)
{ {
var imageTileSource = tileLayer.TileSource as ImageTileSource; var imageTileSource = tileLayer.TileSource as ImageTileSource;
@ -48,7 +48,7 @@ namespace MapControl
} }
} }
public void CancelLoadTiles(TileLayer tileLayer) public void CancelLoadTiles(MapTileLayer tileLayer)
{ {
} }
} }

View file

@ -1,5 +1,5 @@
// XAML Map Control - http://xamlmapcontrol.codeplex.com/ // XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2016 Clemens Fischer // © 2017 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL) // Licensed under the Microsoft Public License (Ms-PL)
using System; using System;
@ -78,7 +78,7 @@ namespace MapControl
private readonly ConcurrentQueue<PendingTile> pendingTiles = new ConcurrentQueue<PendingTile>(); private readonly ConcurrentQueue<PendingTile> pendingTiles = new ConcurrentQueue<PendingTile>();
private int taskCount; private int taskCount;
public void BeginLoadTiles(TileLayer tileLayer, IEnumerable<Tile> tiles) public void BeginLoadTiles(MapTileLayer tileLayer, IEnumerable<Tile> tiles)
{ {
if (tiles.Any()) if (tiles.Any())
{ {
@ -105,7 +105,7 @@ namespace MapControl
} }
} }
public void CancelLoadTiles(TileLayer tileLayer) public void CancelLoadTiles(MapTileLayer tileLayer)
{ {
PendingTile pendingTile; PendingTile pendingTile;
@ -211,7 +211,7 @@ namespace MapControl
{ {
using (var fileStream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read)) using (var fileStream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read))
{ {
image = ImageLoader.FromStream(fileStream); image = BitmapSourceHelper.FromStream(fileStream);
} }
} }
catch (Exception ex) catch (Exception ex)
@ -245,7 +245,7 @@ namespace MapControl
{ {
responseStream.CopyTo(memoryStream); responseStream.CopyTo(memoryStream);
memoryStream.Seek(0, SeekOrigin.Begin); memoryStream.Seek(0, SeekOrigin.Begin);
image = ImageLoader.FromStream(memoryStream); image = BitmapSourceHelper.FromStream(memoryStream);
if (cacheKey != null) if (cacheKey != null)
{ {
@ -284,7 +284,7 @@ namespace MapControl
{ {
using (var memoryStream = new MemoryStream(buffer)) using (var memoryStream = new MemoryStream(buffer))
{ {
image = ImageLoader.FromStream(memoryStream); image = BitmapSourceHelper.FromStream(memoryStream);
} }
DateTime expiration = DateTime.MinValue; DateTime expiration = DateTime.MinValue;

View file

@ -1,5 +1,5 @@
// XAML Map Control - http://xamlmapcontrol.codeplex.com/ // XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2016 Clemens Fischer // © 2017 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL) // Licensed under the Microsoft Public License (Ms-PL)
using System; using System;
@ -71,7 +71,7 @@ namespace MapControl
private readonly ConcurrentQueue<PendingTile> pendingTiles = new ConcurrentQueue<PendingTile>(); private readonly ConcurrentQueue<PendingTile> pendingTiles = new ConcurrentQueue<PendingTile>();
private int taskCount; private int taskCount;
public void BeginLoadTiles(TileLayer tileLayer, IEnumerable<Tile> tiles) public void BeginLoadTiles(MapTileLayer tileLayer, IEnumerable<Tile> tiles)
{ {
var tileSource = tileLayer.TileSource; var tileSource = tileLayer.TileSource;
var imageTileSource = tileSource as ImageTileSource; var imageTileSource = tileSource as ImageTileSource;
@ -127,7 +127,7 @@ namespace MapControl
} }
} }
public void CancelLoadTiles(TileLayer tileLayer) public void CancelLoadTiles(MapTileLayer tileLayer)
{ {
PendingTile pendingTile; PendingTile pendingTile;

View file

@ -1,52 +0,0 @@
// XAML Map Control - http://xamlmapcontrol.codeplex.com/
// © 2016 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
{
public partial class TileLayer
{
partial void Initialize()
{
IsHitTestVisible = false;
MapPanel.AddParentMapHandlers(this);
}
private Rect GetTileIndexBounds(int zoomLevel)
{
var scale = (1 << zoomLevel) / 360d;
var transform = new MatrixTransform
{
Matrix = parentMap.ViewportTransform.Matrix
.Invert() // view to map coordinates
.Translate(180d, -180d)
.Scale(scale, -scale) // map coordinates to tile indices
};
return transform.TransformBounds(new Rect(new Point(), parentMap.RenderSize));
}
private void SetRenderTransform()
{
var scale = Math.Pow(2d, parentMap.ZoomLevel - TileGrid.ZoomLevel);
var offsetX = parentMap.ViewportOrigin.X - (180d + parentMap.MapOrigin.X) * parentMap.ViewportScale;
var offsetY = parentMap.ViewportOrigin.Y - (180d - parentMap.MapOrigin.Y) * parentMap.ViewportScale;
((MatrixTransform)RenderTransform).Matrix =
new Matrix(1d, 0d, 0d, 1d, TileSource.TileSize * TileGrid.XMin, TileSource.TileSize * TileGrid.YMin)
.Scale(scale, scale)
.Translate(offsetX, offsetY)
.RotateAt(parentMap.Heading, parentMap.ViewportOrigin.X, parentMap.ViewportOrigin.Y);
}
}
}

View file

@ -1,45 +0,0 @@
// XAML Map Control - http://xamlmapcontrol.codeplex.com/
// © 2016 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL)
using System;
using System.Windows;
using System.Windows.Media;
namespace MapControl
{
public partial class TileLayer
{
static TileLayer()
{
IsHitTestVisibleProperty.OverrideMetadata(
typeof(TileLayer), new FrameworkPropertyMetadata(false));
}
private Rect GetTileIndexBounds(int zoomLevel)
{
var scale = (1 << zoomLevel) / 360d;
var transform = parentMap.ViewportTransform.Matrix;
transform.Invert(); // view to map coordinates
transform.Translate(180d, -180d);
transform.Scale(scale, -scale); // map coordinates to tile indices
return new MatrixTransform(transform).TransformBounds(new Rect(parentMap.RenderSize));
}
private void SetRenderTransform()
{
var scale = Math.Pow(2d, parentMap.ZoomLevel - TileGrid.ZoomLevel);
var offsetX = parentMap.ViewportOrigin.X - (180d + parentMap.MapOrigin.X) * parentMap.ViewportScale;
var offsetY = parentMap.ViewportOrigin.Y - (180d - parentMap.MapOrigin.Y) * parentMap.ViewportScale;
var transform = new Matrix(1d, 0d, 0d, 1d, TileSource.TileSize * TileGrid.XMin, TileSource.TileSize * TileGrid.YMin);
transform.Scale(scale, scale);
transform.Translate(offsetX, offsetY);
transform.RotateAt(parentMap.Heading, parentMap.ViewportOrigin.X, parentMap.ViewportOrigin.Y);
((MatrixTransform)RenderTransform).Matrix = transform;
}
}
}

View file

@ -1,21 +0,0 @@
// XAML Map Control - http://xamlmapcontrol.codeplex.com/
// © 2016 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL)
using System.Collections.ObjectModel;
using System.Linq;
namespace MapControl
{
/// <summary>
/// A collection of TileLayers with a string indexer that allows
/// to retrieve individual TileLayers by their SourceName property.
/// </summary>
public class TileLayerCollection : Collection<TileLayer>
{
public TileLayer this[string sourceName]
{
get { return this.FirstOrDefault(t => t.SourceName == sourceName); }
}
}
}

View file

@ -1,5 +1,5 @@
// XAML Map Control - http://xamlmapcontrol.codeplex.com/ // XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2016 Clemens Fischer // © 2017 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL) // Licensed under the Microsoft Public License (Ms-PL)
using System; using System;
@ -13,7 +13,6 @@ namespace MapControl
public partial class TileSource public partial class TileSource
{ {
public const int TileSize = 256; public const int TileSize = 256;
public const double MetersPerDegree = 6378137d * Math.PI / 180d; // WGS 84 semi major axis
private Func<int, int, int, Uri> getUri; private Func<int, int, int, Uri> getUri;
private string uriFormat = string.Empty; private string uriFormat = string.Empty;
@ -58,6 +57,10 @@ namespace MapControl
getUri = GetBasicUri; getUri = GetBasicUri;
} }
} }
else if (uriFormat.Contains("{x}") && uriFormat.Contains("{v}") && uriFormat.Contains("{z}"))
{
getUri = GetTmsUri;
}
else if (uriFormat.Contains("{q}")) // {i} is optional else if (uriFormat.Contains("{q}")) // {i} is optional
{ {
getUri = GetQuadKeyUri; getUri = GetQuadKeyUri;
@ -70,10 +73,6 @@ namespace MapControl
{ {
getUri = GetLatLonBoundingBoxUri; getUri = GetLatLonBoundingBoxUri;
} }
else if (uriFormat.Contains("{x}") && uriFormat.Contains("{v}") && uriFormat.Contains("{z}"))
{
getUri = GetTmsUri;
}
} }
} }
@ -161,10 +160,10 @@ namespace MapControl
private Uri GetBoundingBoxUri(int x, int y, int zoomLevel) private Uri GetBoundingBoxUri(int x, int y, int zoomLevel)
{ {
var tileSize = 360d / (1 << zoomLevel); // tile width in degrees var tileSize = 360d / (1 << zoomLevel); // tile width in degrees
var west = MetersPerDegree * (x * tileSize - 180d); var west = MapProjection.MetersPerDegree * (x * tileSize - 180d);
var east = MetersPerDegree * ((x + 1) * tileSize - 180d); var east = MapProjection.MetersPerDegree * ((x + 1) * tileSize - 180d);
var south = MetersPerDegree * (180d - (y + 1) * tileSize); var south = MapProjection.MetersPerDegree * (180d - (y + 1) * tileSize);
var north = MetersPerDegree * (180d - y * tileSize); var north = MapProjection.MetersPerDegree * (180d - y * tileSize);
return new Uri(uriFormat return new Uri(uriFormat
.Replace("{W}", west.ToString(CultureInfo.InvariantCulture)) .Replace("{W}", west.ToString(CultureInfo.InvariantCulture))
@ -180,8 +179,8 @@ namespace MapControl
var tileSize = 360d / (1 << zoomLevel); // tile width in degrees var tileSize = 360d / (1 << zoomLevel); // tile width in degrees
var west = x * tileSize - 180d; var west = x * tileSize - 180d;
var east = (x + 1) * tileSize - 180d; var east = (x + 1) * tileSize - 180d;
var south = MercatorTransform.YToLatitude(180d - (y + 1) * tileSize); var south = WebMercatorProjection.YToLatitude(180d - (y + 1) * tileSize);
var north = MercatorTransform.YToLatitude(180d - y * tileSize); var north = WebMercatorProjection.YToLatitude(180d - y * tileSize);
return new Uri(uriFormat return new Uri(uriFormat
.Replace("{w}", west.ToString(CultureInfo.InvariantCulture)) .Replace("{w}", west.ToString(CultureInfo.InvariantCulture))

View file

@ -1,5 +1,5 @@
// XAML Map Control - http://xamlmapcontrol.codeplex.com/ // XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2016 Clemens Fischer // © 2017 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL) // Licensed under the Microsoft Public License (Ms-PL)
using System; using System;

View file

@ -1,4 +1,4 @@
// XAML Map Control - http://xamlmapcontrol.codeplex.com/ // XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2017 Clemens Fischer // © 2017 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL) // Licensed under the Microsoft Public License (Ms-PL)
@ -8,15 +8,22 @@ namespace MapControl
{ {
public class ViewportChangedEventArgs : EventArgs public class ViewportChangedEventArgs : EventArgs
{ {
public ViewportChangedEventArgs(double originOffset) public ViewportChangedEventArgs(bool projectionChanged = false, double longitudeOffset = 0d)
{ {
OriginOffset = originOffset; ProjectionChanged = projectionChanged;
LongitudeOffset = longitudeOffset;
} }
/// <summary> /// <summary>
/// Offset of the X value of the map coordinate origin from the previous viewport. /// Indicates if the map projection has changed, i.e. if a TileLayer or ImageLayer should be
/// immediately updated, or MapPath Data in cartesian map coordinates should be recalculated.
/// </summary>
public bool ProjectionChanged { get; }
/// <summary>
/// Offset of the map center longitude value from the previous viewport.
/// Used to detect if the map center has moved across 180° longitude. /// Used to detect if the map center has moved across 180° longitude.
/// </summary> /// </summary>
public double OriginOffset { get; } public double LongitudeOffset { get; }
} }
} }

View file

@ -0,0 +1,83 @@
// 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;
#else
using System.Windows;
#endif
namespace MapControl
{
/// <summary>
/// Transforms map coordinates according to the Web Mercator Projection.
/// Longitude values are transformed linearly to X values in meters, by multiplying with MetersPerDegree.
/// Latitude values in the interval [-MaxLatitude .. MaxLatitude] are transformed to Y values in meters
/// in the interval [-R*pi .. R*pi], R=Wgs84EquatorialRadius.
/// </summary>
public class WebMercatorProjection : MapProjection
{
public WebMercatorProjection()
: this("EPSG:3857")
{
}
public WebMercatorProjection(string crsId)
{
CrsId = crsId;
IsWebMercator = true;
LongitudeScale = MetersPerDegree;
MaxLatitude = YToLatitude(180d);
}
public override double GetViewportScale(double zoomLevel)
{
return base.GetViewportScale(zoomLevel) / MetersPerDegree;
}
public override Point GetMapScale(Location location)
{
var scale = ViewportScale / Math.Cos(location.Latitude * Math.PI / 180d);
return new Point(scale, scale);
}
public override Point LocationToPoint(Location location)
{
return new Point(
MetersPerDegree * location.Longitude,
MetersPerDegree * LatitudeToY(location.Latitude));
}
public override Location PointToLocation(Point point)
{
return new Location(
YToLatitude(point.Y / MetersPerDegree),
point.X / MetersPerDegree);
}
public override Location TranslateLocation(Location location, Point translation)
{
var scaleX = MetersPerDegree * ViewportScale;
var scaleY = scaleX / Math.Cos(location.Latitude * Math.PI / 180d);
return new Location(
location.Latitude - translation.Y / scaleY,
location.Longitude + translation.X / scaleX);
}
public static double LatitudeToY(double latitude)
{
return latitude <= -90d ? double.NegativeInfinity
: latitude >= 90d ? double.PositiveInfinity
: Math.Log(Math.Tan((latitude + 90d) * Math.PI / 360d)) / Math.PI * 180d;
}
public static double YToLatitude(double y)
{
return Math.Atan(Math.Exp(y * Math.PI / 180d)) / Math.PI * 360d - 90d;
}
}
}

View file

@ -36,15 +36,33 @@
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Compile Include="..\AzimuthalEquidistantProjection.cs">
<Link>AzimuthalEquidistantProjection.cs</Link>
</Compile>
<Compile Include="..\AzimuthalProjection.cs">
<Link>AzimuthalProjection.cs</Link>
</Compile>
<Compile Include="..\BingMapsTileLayer.cs"> <Compile Include="..\BingMapsTileLayer.cs">
<Link>BingMapsTileLayer.cs</Link> <Link>BingMapsTileLayer.cs</Link>
</Compile> </Compile>
<Compile Include="..\BingMapsTileSource.cs"> <Compile Include="..\BingMapsTileSource.cs">
<Link>BingMapsTileSource.cs</Link> <Link>BingMapsTileSource.cs</Link>
</Compile> </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"> <Compile Include="..\Extensions.WinRT.cs">
<Link>Extensions.WinRT.cs</Link> <Link>Extensions.WinRT.cs</Link>
</Compile> </Compile>
<Compile Include="..\GnomonicProjection.cs">
<Link>GnomonicProjection.cs</Link>
</Compile>
<Compile Include="..\HyperlinkText.cs"> <Compile Include="..\HyperlinkText.cs">
<Link>HyperlinkText.cs</Link> <Link>HyperlinkText.cs</Link>
</Compile> </Compile>
@ -54,12 +72,6 @@
<Compile Include="..\ImageTileSource.Silverlight.WinRT.cs"> <Compile Include="..\ImageTileSource.Silverlight.WinRT.cs">
<Link>ImageTileSource.Silverlight.WinRT.cs</Link> <Link>ImageTileSource.Silverlight.WinRT.cs</Link>
</Compile> </Compile>
<Compile Include="..\IMapElement.cs">
<Link>IMapElement.cs</Link>
</Compile>
<Compile Include="..\ITileImageLoader.cs">
<Link>ITileImageLoader.cs</Link>
</Compile>
<Compile Include="..\Location.cs"> <Compile Include="..\Location.cs">
<Link>Location.cs</Link> <Link>Location.cs</Link>
</Compile> </Compile>
@ -81,9 +93,6 @@
<Compile Include="..\MapGraticule.Silverlight.WinRT.cs"> <Compile Include="..\MapGraticule.Silverlight.WinRT.cs">
<Link>MapGraticule.Silverlight.WinRT.cs</Link> <Link>MapGraticule.Silverlight.WinRT.cs</Link>
</Compile> </Compile>
<Compile Include="..\MapImage.cs">
<Link>MapImage.cs</Link>
</Compile>
<Compile Include="..\MapImageLayer.cs"> <Compile Include="..\MapImageLayer.cs">
<Link>MapImageLayer.cs</Link> <Link>MapImageLayer.cs</Link>
</Compile> </Compile>
@ -120,24 +129,27 @@
<Compile Include="..\MapPolyline.Silverlight.WinRT.cs"> <Compile Include="..\MapPolyline.Silverlight.WinRT.cs">
<Link>MapPolyline.Silverlight.WinRT.cs</Link> <Link>MapPolyline.Silverlight.WinRT.cs</Link>
</Compile> </Compile>
<Compile Include="..\MapRectangle.cs"> <Compile Include="..\MapProjection.cs">
<Link>MapRectangle.cs</Link> <Link>MapProjection.cs</Link>
</Compile> </Compile>
<Compile Include="..\MapTransform.cs"> <Compile Include="..\MapTileLayer.cs">
<Link>MapTransform.cs</Link> <Link>MapTileLayer.cs</Link>
</Compile> </Compile>
<Compile Include="..\MatrixEx.cs"> <Compile Include="..\MapTileLayer.Silverlight.WinRT.cs">
<Link>MatrixEx.cs</Link> <Link>MapTileLayer.Silverlight.WinRT.cs</Link>
</Compile> </Compile>
<Compile Include="..\MercatorTransform.cs"> <Compile Include="..\MatrixEx.Silverlight.WinRT.cs">
<Link>MercatorTransform.cs</Link> <Link>MatrixEx.Silverlight.WinRT.cs</Link>
</Compile> </Compile>
<Compile Include="..\PanelBase.cs"> <Compile Include="..\OrthographicProjection.cs">
<Link>PanelBase.cs</Link> <Link>OrthographicProjection.cs</Link>
</Compile> </Compile>
<Compile Include="..\Pushpin.Silverlight.WinRT.cs"> <Compile Include="..\Pushpin.Silverlight.WinRT.cs">
<Link>Pushpin.Silverlight.WinRT.cs</Link> <Link>Pushpin.Silverlight.WinRT.cs</Link>
</Compile> </Compile>
<Compile Include="..\StereographicProjection.cs">
<Link>StereographicProjection.cs</Link>
</Compile>
<Compile Include="..\Tile.cs"> <Compile Include="..\Tile.cs">
<Link>Tile.cs</Link> <Link>Tile.cs</Link>
</Compile> </Compile>
@ -150,26 +162,20 @@
<Compile Include="..\TileImageLoader.WinRT.cs"> <Compile Include="..\TileImageLoader.WinRT.cs">
<Link>TileImageLoader.WinRT.cs</Link> <Link>TileImageLoader.WinRT.cs</Link>
</Compile> </Compile>
<Compile Include="..\TileLayer.cs">
<Link>TileLayer.cs</Link>
</Compile>
<Compile Include="..\TileLayer.Silverlight.WinRT.cs">
<Link>TileLayer.Silverlight.WinRT.cs</Link>
</Compile>
<Compile Include="..\TileLayerCollection.cs">
<Link>TileLayerCollection.cs</Link>
</Compile>
<Compile Include="..\TileSource.cs"> <Compile Include="..\TileSource.cs">
<Link>TileSource.cs</Link> <Link>TileSource.cs</Link>
</Compile> </Compile>
<Compile Include="..\ViewportChangedEventArgs.cs"> <Compile Include="..\ViewportChangedEventArgs.cs">
<Link>ViewportChangedEventArgs.cs</Link> <Link>ViewportChangedEventArgs.cs</Link>
</Compile> </Compile>
<Compile Include="..\WebMercatorProjection.cs">
<Link>WebMercatorProjection.cs</Link>
</Compile>
<Compile Include="..\WmsImageLayer.cs"> <Compile Include="..\WmsImageLayer.cs">
<Link>WmsImageLayer.cs</Link> <Link>WmsImageLayer.cs</Link>
</Compile> </Compile>
<Compile Include="..\WmsImageLayer.WinRT.cs"> <Compile Include="..\WmsLayers.WPF.WinRT.cs">
<Link>WmsImageLayer.WinRT.cs</Link> <Link>WmsLayers.WPF.WinRT.cs</Link>
</Compile> </Compile>
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup> </ItemGroup>

View file

@ -5,10 +5,10 @@ using System.Runtime.InteropServices;
[assembly: AssemblyDescription("XAML Map Control Library for Windows Runtime")] [assembly: AssemblyDescription("XAML Map Control Library for Windows Runtime")]
[assembly: AssemblyProduct("XAML Map Control")] [assembly: AssemblyProduct("XAML Map Control")]
[assembly: AssemblyCompany("Clemens Fischer")] [assembly: AssemblyCompany("Clemens Fischer")]
[assembly: AssemblyCopyright("© 2016 Clemens Fischer")] [assembly: AssemblyCopyright("© 2017 Clemens Fischer")]
[assembly: AssemblyTrademark("")] [assembly: AssemblyTrademark("")]
[assembly: AssemblyVersion("2.14.0")] [assembly: AssemblyVersion("3.1.0")]
[assembly: AssemblyFileVersion("2.14.0")] [assembly: AssemblyFileVersion("3.1.0")]
[assembly: AssemblyConfiguration("")] [assembly: AssemblyConfiguration("")]
[assembly: AssemblyCulture("")] [assembly: AssemblyCulture("")]
[assembly: ComVisible(false)] [assembly: ComVisible(false)]

View file

@ -1,7 +1,8 @@
// XAML Map Control - http://xamlmapcontrol.codeplex.com/ // XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2016 Clemens Fischer // © 2017 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL) // Licensed under the Microsoft Public License (Ms-PL)
using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Xml; using System.Xml;
@ -9,10 +10,10 @@ namespace MapControl
{ {
public partial class WmsImageLayer public partial class WmsImageLayer
{ {
private static async Task<XmlDocument> LoadDocument(string requestUri) private static async Task<XmlDocument> LoadDocument(Uri requestUri)
{ {
var document = new XmlDocument(); var document = new XmlDocument();
await Task.Run(() => document.Load(requestUri)); await Task.Run(() => document.Load(requestUri.ToString()));
return document; return document;
} }
} }

View file

@ -1,5 +1,5 @@
// XAML Map Control - http://xamlmapcontrol.codeplex.com/ // XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2016 Clemens Fischer // © 2017 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL) // Licensed under the Microsoft Public License (Ms-PL)
using System; using System;
@ -10,9 +10,9 @@ namespace MapControl
{ {
public partial class WmsImageLayer public partial class WmsImageLayer
{ {
private static async Task<XmlDocument> LoadDocument(string requestUri) private static async Task<XmlDocument> LoadDocument(Uri requestUri)
{ {
return await XmlDocument.LoadFromUriAsync(new Uri(requestUri)); return await XmlDocument.LoadFromUriAsync(requestUri);
} }
} }
} }

View file

@ -1,44 +1,58 @@
// XAML Map Control - http://xamlmapcontrol.codeplex.com/ // XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2016 Clemens Fischer // © 2017 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL) // Licensed under the Microsoft Public License (Ms-PL)
using System; using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
#if NETFX_CORE #if NETFX_CORE
using Windows.Data.Xml.Dom;
using Windows.UI.Xaml; using Windows.UI.Xaml;
#else #else
using System.Windows; using System.Windows;
using System.Xml;
#endif #endif
namespace MapControl namespace MapControl
{ {
public partial class WmsImageLayer : MapImageLayer public partial class WmsImageLayer : MapImageLayer
{ {
public static readonly DependencyProperty BaseUriProperty = DependencyProperty.Register( public static readonly DependencyProperty ServerUriProperty = DependencyProperty.Register(
"ServerUri", typeof(string), typeof(WmsImageLayer), nameof(ServerUri), typeof(Uri), typeof(WmsImageLayer),
new PropertyMetadata(null, async (o, e) => await ((WmsImageLayer)o).UpdateUriFormat(true))); new PropertyMetadata(null, (o, e) => ((WmsImageLayer)o).UpdateImage()));
public static readonly DependencyProperty VersionProperty = DependencyProperty.Register(
nameof(Version), typeof(string), typeof(WmsImageLayer),
new PropertyMetadata("1.3.0", (o, e) => ((WmsImageLayer)o).UpdateImage()));
public static readonly DependencyProperty LayersProperty = DependencyProperty.Register( public static readonly DependencyProperty LayersProperty = DependencyProperty.Register(
"Layers", typeof(string), typeof(WmsImageLayer), nameof(Layers), typeof(string), typeof(WmsImageLayer),
new PropertyMetadata(null, async (o, e) => await ((WmsImageLayer)o).UpdateUriFormat())); new PropertyMetadata(null, (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( public static readonly DependencyProperty ParametersProperty = DependencyProperty.Register(
"Parameters", typeof(string), typeof(WmsImageLayer), nameof(Parameters), typeof(string), typeof(WmsImageLayer),
new PropertyMetadata(null, async (o, e) => await ((WmsImageLayer)o).UpdateUriFormat())); new PropertyMetadata(null, (o, e) => ((WmsImageLayer)o).UpdateImage()));
public static readonly DependencyProperty FormatProperty = DependencyProperty.Register(
nameof(Format), typeof(string), typeof(WmsImageLayer),
new PropertyMetadata("image/png", (o, e) => ((WmsImageLayer)o).UpdateImage()));
public static readonly DependencyProperty TransparentProperty = DependencyProperty.Register( public static readonly DependencyProperty TransparentProperty = DependencyProperty.Register(
"Transparent", typeof(bool), typeof(WmsImageLayer), nameof(Transparent), typeof(bool), typeof(WmsImageLayer),
new PropertyMetadata(false, async (o, e) => await ((WmsImageLayer)o).UpdateUriFormat())); new PropertyMetadata(false, (o, e) => ((WmsImageLayer)o).UpdateImage()));
public string ServerUri private string layers = string.Empty;
public Uri ServerUri
{ {
get { return (string)GetValue(BaseUriProperty); } get { return (Uri)GetValue(ServerUriProperty); }
set { SetValue(BaseUriProperty, value); } set { SetValue(ServerUriProperty, value); }
}
public string Version
{
get { return (string)GetValue(VersionProperty); }
set { SetValue(VersionProperty, value); }
} }
public string Layers public string Layers
@ -47,106 +61,63 @@ namespace MapControl
set { SetValue(LayersProperty, value); } set { SetValue(LayersProperty, value); }
} }
public string Styles
{
get { return (string)GetValue(StylesProperty); }
set { SetValue(StylesProperty, value); }
}
public string Parameters public string Parameters
{ {
get { return (string)GetValue(ParametersProperty); } get { return (string)GetValue(ParametersProperty); }
set { SetValue(ParametersProperty, value); } set { SetValue(ParametersProperty, value); }
} }
public string Format
{
get { return (string)GetValue(FormatProperty); }
set { SetValue(FormatProperty, value); }
}
public bool Transparent public bool Transparent
{ {
get { return (bool)GetValue(TransparentProperty); } get { return (bool)GetValue(TransparentProperty); }
set { SetValue(TransparentProperty, value); } set { SetValue(TransparentProperty, value); }
} }
public async Task<List<string>> GetAllLayers() protected override bool UpdateImage(BoundingBox boundingBox)
{ {
var layers = new List<string>(); if (ServerUri == null)
if (!string.IsNullOrEmpty(ServerUri))
{ {
try return false;
{
var document = await LoadDocument(ServerUri
+ "?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)
{
layers.Add(name.InnerText);
}
}
}
}
}
catch (Exception e)
{
Debug.WriteLine(e.Message);
}
} }
return layers; var version = Version ?? "1.3.0";
} var queryParameters = ParentMap.MapProjection.WmsQueryParameters(boundingBox, version);
private string allLayers; if (string.IsNullOrEmpty(queryParameters))
private async Task UpdateUriFormat(bool baseUriChanged = false)
{
if (baseUriChanged)
{ {
allLayers = null; return false;
} }
string uriFormat = null; var query = "?SERVICE=WMS"
var layers = Layers; + "&VERSION=" + version
+ "&REQUEST=GetMap"
+ "&LAYERS=" + (Layers ?? string.Empty)
+ "&STYLES=" + (Styles ?? string.Empty)
+ "&" + queryParameters
+ "&FORMAT=" + (Format ?? "image/png")
+ "&TRANSPARENT=" + (Transparent ? "TRUE" : "FALSE");
if (!string.IsNullOrEmpty(ServerUri) && !string.IsNullOrEmpty(layers)) if (!string.IsNullOrEmpty(Parameters))
{ {
if (layers == "*") query += "&" + Parameters;
{
layers = allLayers ?? (allLayers = string.Join(",", await GetAllLayers()));
}
uriFormat = ServerUri
+ "?SERVICE=WMS"
+ "&VERSION=1.3.0"
+ "&REQUEST=GetMap"
+ "&LAYERS=" + layers.Replace(" ", "%20")
+ "&STYLES="
+ "&CRS=EPSG:3857"
+ "&BBOX={W},{S},{E},{N}"
+ "&WIDTH={X}"
+ "&HEIGHT={Y}"
+ "&FORMAT=image/png"
+ "&TRANSPARENT=" + (Transparent ? "TRUE" : "FALSE");
if (!string.IsNullOrEmpty(Parameters))
{
uriFormat += "&" + Parameters;
}
} }
UriFormat = uriFormat; var uri = new Uri(ServerUri, query.Replace(" ", "%20"));
}
private static IEnumerable<XmlElement> ChildElements(XmlElement element, string name) UpdateImage(uri);
{ return true;
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);
} }
} }
} }

View file

@ -0,0 +1,77 @@
// 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 document = await LoadDocument(new Uri(serverUri, "?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
}
}
}

Some files were not shown because too many files have changed in this diff Show more