Version 2.6.0: -replaced static fields by static properties and dependency properties

- improved class TileLayer
- MapGraticule with arc seconds
This commit is contained in:
ClemensF 2015-11-11 19:48:50 +01:00
parent 43e87f26ba
commit bc30e1d9ca
40 changed files with 337 additions and 266 deletions

View file

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

View file

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

View file

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

View file

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

View file

@ -1,43 +0,0 @@
// XAML Map Control - http://xamlmapcontrol.codeplex.com/
// © 2015 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL)
namespace MapControl
{
public struct Int32Rect
{
public Int32Rect(int x, int y, int width, int height)
: this()
{
X = x;
Y = y;
Width = width;
Height = height;
}
public int X { get; set; }
public int Y { get; set; }
public int Width { get; set; }
public int Height { get; set; }
public override int GetHashCode()
{
return X ^ Y ^ Width ^ Height;
}
public override bool Equals(object obj)
{
return obj is Int32Rect && (Int32Rect)obj == this;
}
public static bool operator ==(Int32Rect rect1, Int32Rect rect2)
{
return rect1.X == rect2.X && rect1.Y == rect2.Y && rect1.Width == rect2.Width && rect1.Height == rect2.Height;
}
public static bool operator !=(Int32Rect rect1, Int32Rect rect2)
{
return !(rect1 == rect2);
}
}
}

View file

@ -20,31 +20,32 @@ 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), new PropertyMetadata(new SolidColorBrush(Colors.Black))); "Foreground", typeof(Brush), typeof(MapBase),
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), new PropertyMetadata(new Location(), "Center", typeof(Location), typeof(MapBase),
(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), new PropertyMetadata(new Location(), "TargetCenter", typeof(Location), typeof(MapBase),
(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), new PropertyMetadata(1d, "ZoomLevel", typeof(double), typeof(MapBase),
(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), new PropertyMetadata(1d, "TargetZoomLevel", typeof(double), typeof(MapBase),
(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), new PropertyMetadata(0d, "Heading", typeof(double), typeof(MapBase),
(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), new PropertyMetadata(0d, "TargetHeading", typeof(double), typeof(MapBase),
(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()
{ {
@ -67,7 +68,7 @@ namespace MapControl
private void SetViewportTransform(Location origin) private void SetViewportTransform(Location origin)
{ {
MapOrigin = mapTransform.Transform(origin); MapOrigin = mapTransform.Transform(origin);
ViewportScale = Math.Pow(2d, ZoomLevel) * TileSource.TileSize / 360d; ViewportScale = Math.Pow(2d, ZoomLevel) * (double)TileSource.TileSize / 360d;
viewportTransform.Matrix = viewportTransform.Matrix =
new Matrix(1d, 0d, 0d, 1d, -MapOrigin.X, -MapOrigin.Y) new Matrix(1d, 0d, 0d, 1d, -MapOrigin.X, -MapOrigin.Y)

View file

@ -46,10 +46,10 @@ namespace MapControl
static MapBase() static MapBase()
{ {
UIElement.ClipToBoundsProperty.OverrideMetadata( ClipToBoundsProperty.OverrideMetadata(
typeof(MapBase), new FrameworkPropertyMetadata(true)); typeof(MapBase), new FrameworkPropertyMetadata(true));
Panel.BackgroundProperty.OverrideMetadata( BackgroundProperty.OverrideMetadata(
typeof(MapBase), new FrameworkPropertyMetadata(Brushes.Transparent)); typeof(MapBase), new FrameworkPropertyMetadata(Brushes.Transparent));
} }
@ -69,7 +69,7 @@ namespace MapControl
private void SetViewportTransform(Location origin) private void SetViewportTransform(Location origin)
{ {
MapOrigin = mapTransform.Transform(origin); MapOrigin = mapTransform.Transform(origin);
ViewportScale = Math.Pow(2d, ZoomLevel) * TileSource.TileSize / 360d; ViewportScale = Math.Pow(2d, ZoomLevel) * (double)TileSource.TileSize / 360d;
var transform = new Matrix(1d, 0d, 0d, 1d, -MapOrigin.X, -MapOrigin.Y); var transform = new Matrix(1d, 0d, 0d, 1d, -MapOrigin.X, -MapOrigin.Y);
transform.Scale(ViewportScale, -ViewportScale); transform.Scale(ViewportScale, -ViewportScale);

View file

@ -30,28 +30,37 @@ namespace MapControl
{ {
private const double MaximumZoomLevel = 22d; private const double MaximumZoomLevel = 22d;
public static TimeSpan AnimationDuration = TimeSpan.FromSeconds(0.3);
public static EasingFunctionBase AnimationEasingFunction = new QuadraticEase { EasingMode = EasingMode.EaseOut };
public static readonly DependencyProperty TileLayerProperty = DependencyProperty.Register( public static readonly DependencyProperty TileLayerProperty = DependencyProperty.Register(
"TileLayer", typeof(TileLayer), typeof(MapBase), new PropertyMetadata(null, "TileLayer", typeof(TileLayer), typeof(MapBase),
(o, e) => ((MapBase)o).TileLayerPropertyChanged((TileLayer)e.NewValue))); new PropertyMetadata(null, (o, e) => ((MapBase)o).TileLayerPropertyChanged((TileLayer)e.NewValue)));
public static readonly DependencyProperty TileLayersProperty = DependencyProperty.Register( public static readonly DependencyProperty TileLayersProperty = DependencyProperty.Register(
"TileLayers", typeof(IList<TileLayer>), typeof(MapBase), new PropertyMetadata(null, "TileLayers", typeof(IList<TileLayer>), typeof(MapBase),
(o, e) => ((MapBase)o).TileLayersPropertyChanged((IList<TileLayer>)e.OldValue, (IList<TileLayer>)e.NewValue))); new PropertyMetadata(null, (o, e) => ((MapBase)o).TileLayersPropertyChanged((IList<TileLayer>)e.OldValue, (IList<TileLayer>)e.NewValue)));
public static readonly DependencyProperty MinZoomLevelProperty = DependencyProperty.Register( public static readonly DependencyProperty MinZoomLevelProperty = DependencyProperty.Register(
"MinZoomLevel", typeof(double), typeof(MapBase), new PropertyMetadata(1d, "MinZoomLevel", typeof(double), typeof(MapBase),
(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), new PropertyMetadata(19d, "MaxZoomLevel", typeof(double), typeof(MapBase),
(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(
"AnimationDuration", typeof(TimeSpan), typeof(MapBase),
new PropertyMetadata(TimeSpan.FromSeconds(0.3)));
public static readonly DependencyProperty AnimationEasingFunctionProperty = DependencyProperty.Register(
"AnimationEasingFunction", typeof(EasingFunctionBase), typeof(MapBase),
new PropertyMetadata(new QuadraticEase { EasingMode = EasingMode.EaseOut }));
public static readonly DependencyProperty TileFadeDurationProperty = DependencyProperty.Register(
"TileFadeDuration", typeof(TimeSpan), typeof(MapBase),
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), new PropertyMetadata(new Point(), "CenterPoint", typeof(Point), typeof(MapBase),
(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 PanelBase tileLayerPanel = new PanelBase();
private readonly MapTransform mapTransform = new MercatorTransform(); private readonly MapTransform mapTransform = new MercatorTransform();
@ -138,6 +147,7 @@ namespace MapControl
/// <summary> /// <summary>
/// Gets or sets the minimum value of the ZoomLevel and TargetZommLevel properties. /// Gets or sets the minimum value of the ZoomLevel and TargetZommLevel properties.
/// Must be greater than or equal to zero and less than or equal to MaxZoomLevel. /// Must be greater than or equal to zero and less than or equal to MaxZoomLevel.
/// The default value is 1.
/// </summary> /// </summary>
public double MinZoomLevel public double MinZoomLevel
{ {
@ -148,6 +158,7 @@ namespace MapControl
/// <summary> /// <summary>
/// Gets or sets the maximum value of the ZoomLevel and TargetZommLevel properties. /// Gets or sets the maximum value of the ZoomLevel and TargetZommLevel properties.
/// Must be greater than or equal to MinZoomLevel and less than or equal to 20. /// Must be greater than or equal to MinZoomLevel and less than or equal to 20.
/// The default value is 19.
/// </summary> /// </summary>
public double MaxZoomLevel public double MaxZoomLevel
{ {
@ -191,6 +202,36 @@ namespace MapControl
set { SetValue(TargetHeadingProperty, value); } set { SetValue(TargetHeadingProperty, value); }
} }
/// <summary>
/// Gets or sets the Duration of the Center, ZoomLevel and Heading animations.
/// The default value is 0.3 seconds.
/// </summary>
public TimeSpan AnimationDuration
{
get { return (TimeSpan)GetValue(AnimationDurationProperty); }
set { SetValue(AnimationDurationProperty, value); }
}
/// <summary>
/// Gets or sets the EasingFunction of the Center, ZoomLevel and Heading animations.
/// The default value is a QuadraticEase with EasingMode.EaseOut.
/// </summary>
public EasingFunctionBase AnimationEasingFunction
{
get { return (EasingFunctionBase)GetValue(AnimationEasingFunctionProperty); }
set { SetValue(AnimationEasingFunctionProperty, value); }
}
/// <summary>
/// Gets or sets the Duration of the Tile Opacity animation.
/// The default value is 0.2 seconds.
/// </summary>
public TimeSpan TileFadeDuration
{
get { return (TimeSpan)GetValue(TileFadeDurationProperty); }
set { SetValue(TileFadeDurationProperty, value); }
}
/// <summary> /// <summary>
/// Gets the transformation from geographic coordinates to cartesian map coordinates. /// Gets the transformation from geographic coordinates to cartesian map coordinates.
/// </summary> /// </summary>
@ -247,7 +288,7 @@ namespace MapControl
public double GetMapScale(Location location) public double GetMapScale(Location location)
{ {
return mapTransform.RelativeScale(location) * return mapTransform.RelativeScale(location) *
Math.Pow(2d, ZoomLevel) * TileSource.TileSize / (TileSource.MetersPerDegree * 360d); Math.Pow(2d, ZoomLevel) * (double)TileSource.TileSize / (TileSource.MetersPerDegree * 360d);
} }
/// <summary> /// <summary>
@ -364,8 +405,8 @@ namespace MapControl
{ {
var p1 = mapTransform.Transform(southWest); var p1 = mapTransform.Transform(southWest);
var p2 = mapTransform.Transform(northEast); var p2 = mapTransform.Transform(northEast);
var lonScale = RenderSize.Width / (p2.X - p1.X) * 360d / TileSource.TileSize; var lonScale = RenderSize.Width / (p2.X - p1.X) * 360d / (double)TileSource.TileSize;
var latScale = RenderSize.Height / (p2.Y - p1.Y) * 360d / TileSource.TileSize; var latScale = RenderSize.Height / (p2.Y - p1.Y) * 360d / (double)TileSource.TileSize;
var lonZoom = Math.Log(lonScale, 2d); var lonZoom = Math.Log(lonScale, 2d);
var latZoom = Math.Log(latScale, 2d); var latZoom = Math.Log(latScale, 2d);
@ -764,17 +805,12 @@ namespace MapControl
private void UpdateTransform(bool resetTransformOrigin = false) private void UpdateTransform(bool resetTransformOrigin = false)
{ {
Location center; var center = transformOrigin ?? Center;
if (transformOrigin == null) SetViewportTransform(center);
{
center = Center;
SetViewportTransform(center);
}
else
{
SetViewportTransform(transformOrigin);
if (transformOrigin != null)
{
center = ViewportPointToLocation(new Point(RenderSize.Width / 2d, RenderSize.Height / 2d)); center = ViewportPointToLocation(new Point(RenderSize.Width / 2d, RenderSize.Height / 2d));
center.Longitude = Location.NormalizeLongitude(center.Longitude); center.Longitude = Location.NormalizeLongitude(center.Longitude);
@ -799,7 +835,7 @@ namespace MapControl
} }
} }
CenterScale = ViewportScale * mapTransform.RelativeScale(center) / TileSource.MetersPerDegree; // pixels per meter at center latitude CenterScale = ViewportScale * mapTransform.RelativeScale(center) / TileSource.MetersPerDegree;
scaleTransform.ScaleX = CenterScale; scaleTransform.ScaleX = CenterScale;
scaleTransform.ScaleY = CenterScale; scaleTransform.ScaleY = CenterScale;
rotateTransform.Angle = Heading; rotateTransform.Angle = Heading;

View file

@ -74,7 +74,6 @@
<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="IMapElement.cs" />
<Compile Include="Int32Rect.cs" />
<Compile Include="ITileImageLoader.cs" /> <Compile Include="ITileImageLoader.cs" />
<Compile Include="Location.cs" /> <Compile Include="Location.cs" />
<Compile Include="LocationCollection.cs" /> <Compile Include="LocationCollection.cs" />
@ -107,6 +106,7 @@
<Compile Include="Pushpin.Silverlight.WinRT.cs" /> <Compile Include="Pushpin.Silverlight.WinRT.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="TileImageLoader.Silverlight.cs" /> <Compile Include="TileImageLoader.Silverlight.cs" />
<Compile Include="TileLayer.cs" /> <Compile Include="TileLayer.cs" />
<Compile Include="TileLayer.Silverlight.WinRT.cs" /> <Compile Include="TileLayer.Silverlight.WinRT.cs" />

View file

@ -90,6 +90,7 @@
<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="TileImageLoader.WPF.cs" /> <Compile Include="TileImageLoader.WPF.cs" />
<Compile Include="TileLayer.cs" /> <Compile Include="TileLayer.cs" />
<Compile Include="TileLayer.WPF.cs" /> <Compile Include="TileLayer.WPF.cs" />

View file

@ -57,29 +57,23 @@ namespace MapControl
var bounds = ParentMap.ViewportTransform.Inverse.TransformBounds(new Rect(new Point(), ParentMap.RenderSize)); var bounds = ParentMap.ViewportTransform.Inverse.TransformBounds(new Rect(new Point(), ParentMap.RenderSize));
var start = ParentMap.MapTransform.Transform(new Point(bounds.X, bounds.Y)); 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 end = ParentMap.MapTransform.Transform(new Point(bounds.X + bounds.Width, bounds.Y + bounds.Height));
var minSpacing = MinLineSpacing * 360d / (Math.Pow(2d, ParentMap.ZoomLevel) * 256d); var lineDistance = GetLineDistance();
var spacing = LineSpacings[LineSpacings.Length - 1];
if (spacing >= minSpacing)
{
spacing = LineSpacings.FirstOrDefault(s => s >= minSpacing);
}
var labelStart = new Location( var labelStart = new Location(
Math.Ceiling(start.Latitude / spacing) * spacing, Math.Ceiling(start.Latitude / lineDistance) * lineDistance,
Math.Ceiling(start.Longitude / spacing) * spacing); Math.Ceiling(start.Longitude / lineDistance) * lineDistance);
var labelEnd = new Location( var labelEnd = new Location(
Math.Floor(end.Latitude / spacing) * spacing, Math.Floor(end.Latitude / lineDistance) * lineDistance,
Math.Floor(end.Longitude / spacing) * spacing); Math.Floor(end.Longitude / lineDistance) * lineDistance);
var lineStart = new Location( var lineStart = new Location(
Math.Min(Math.Max(labelStart.Latitude - spacing, -ParentMap.MapTransform.MaxLatitude), ParentMap.MapTransform.MaxLatitude), Math.Min(Math.Max(labelStart.Latitude - lineDistance, -ParentMap.MapTransform.MaxLatitude), ParentMap.MapTransform.MaxLatitude),
labelStart.Longitude - spacing); labelStart.Longitude - lineDistance);
var lineEnd = new Location( var lineEnd = new Location(
Math.Min(Math.Max(labelEnd.Latitude + spacing, -ParentMap.MapTransform.MaxLatitude), ParentMap.MapTransform.MaxLatitude), Math.Min(Math.Max(labelEnd.Latitude + lineDistance, -ParentMap.MapTransform.MaxLatitude), ParentMap.MapTransform.MaxLatitude),
labelEnd.Longitude + spacing); labelEnd.Longitude + lineDistance);
if (!lineStart.Equals(graticuleStart) || !lineEnd.Equals(graticuleEnd)) if (!lineStart.Equals(graticuleStart) || !lineEnd.Equals(graticuleEnd))
{ {
@ -90,7 +84,7 @@ namespace MapControl
geometry.Figures.Clear(); geometry.Figures.Clear();
geometry.Transform = ParentMap.ViewportTransform; geometry.Transform = ParentMap.ViewportTransform;
for (var lat = labelStart.Latitude; lat <= end.Latitude; lat += spacing) for (var lat = labelStart.Latitude; lat <= end.Latitude; lat += lineDistance)
{ {
var figure = new PathFigure var figure = new PathFigure
{ {
@ -107,7 +101,7 @@ namespace MapControl
geometry.Figures.Add(figure); geometry.Figures.Add(figure);
} }
for (var lon = labelStart.Longitude; lon <= end.Longitude; lon += spacing) for (var lon = labelStart.Longitude; lon <= end.Longitude; lon += lineDistance)
{ {
var figure = new PathFigure var figure = new PathFigure
{ {
@ -124,12 +118,12 @@ namespace MapControl
geometry.Figures.Add(figure); geometry.Figures.Add(figure);
} }
var labelFormat = GetLabelFormat(lineDistance);
var childIndex = 1; // 0 for Path var childIndex = 1; // 0 for Path
var format = spacing < 1d ? "{0} {1}°{2:00}'" : "{0} {1}°";
for (var lat = labelStart.Latitude; lat <= end.Latitude; lat += spacing) for (var lat = labelStart.Latitude; lat <= end.Latitude; lat += lineDistance)
{ {
for (var lon = labelStart.Longitude; lon <= end.Longitude; lon += spacing) for (var lon = labelStart.Longitude; lon <= end.Longitude; lon += lineDistance)
{ {
TextBlock label; TextBlock label;
@ -169,7 +163,7 @@ namespace MapControl
label.FontStyle = FontStyle; label.FontStyle = FontStyle;
label.FontStretch = FontStretch; label.FontStretch = FontStretch;
label.FontWeight = FontWeight; label.FontWeight = FontWeight;
label.Text = string.Format("{0}\n{1}", CoordinateString(lat, format, "NS"), CoordinateString(Location.NormalizeLongitude(lon), format, "EW")); label.Text = GetLabelText(lat, labelFormat, "NS") + "\n" + GetLabelText(Location.NormalizeLongitude(lon), labelFormat, "EW");
label.Tag = new Location(lat, lon); label.Tag = new Location(lat, lon);
label.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); label.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));

View file

@ -5,7 +5,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.Linq;
using System.Windows; using System.Windows;
using System.Windows.Media; using System.Windows.Media;
@ -27,10 +26,10 @@ namespace MapControl
static MapGraticule() static MapGraticule()
{ {
UIElement.IsHitTestVisibleProperty.OverrideMetadata( IsHitTestVisibleProperty.OverrideMetadata(
typeof(MapGraticule), new FrameworkPropertyMetadata(false)); typeof(MapGraticule), new FrameworkPropertyMetadata(false));
MapOverlay.StrokeThicknessProperty.OverrideMetadata( StrokeThicknessProperty.OverrideMetadata(
typeof(MapGraticule), new FrameworkPropertyMetadata(0.5)); typeof(MapGraticule), new FrameworkPropertyMetadata(0.5));
} }
@ -46,24 +45,17 @@ namespace MapControl
var bounds = ParentMap.ViewportTransform.Inverse.TransformBounds(new Rect(ParentMap.RenderSize)); var bounds = ParentMap.ViewportTransform.Inverse.TransformBounds(new Rect(ParentMap.RenderSize));
var start = ParentMap.MapTransform.Transform(new Point(bounds.X, bounds.Y)); 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 end = ParentMap.MapTransform.Transform(new Point(bounds.X + bounds.Width, bounds.Y + bounds.Height));
var minSpacing = MinLineSpacing * 360d / (Math.Pow(2d, ParentMap.ZoomLevel) * TileSource.TileSize); var lineDistance = GetLineDistance();
var spacing = LineSpacings[LineSpacings.Length - 1]; var labelFormat = GetLabelFormat(lineDistance);
var latLabelStart = Math.Ceiling(start.Latitude / lineDistance) * lineDistance;
var lonLabelStart = Math.Ceiling(start.Longitude / lineDistance) * lineDistance;
var latLabels = new List<Label>((int)((end.Latitude - latLabelStart) / lineDistance) + 1);
var lonLabels = new List<Label>((int)((end.Longitude - lonLabelStart) / lineDistance) + 1);
if (spacing >= minSpacing) for (var lat = latLabelStart; lat <= end.Latitude; lat += lineDistance)
{
spacing = LineSpacings.FirstOrDefault(s => s >= minSpacing);
}
var latLabelStart = Math.Ceiling(start.Latitude / spacing) * spacing;
var lonLabelStart = Math.Ceiling(start.Longitude / spacing) * spacing;
var latLabels = new List<Label>((int)((end.Latitude - latLabelStart) / spacing) + 1);
var lonLabels = new List<Label>((int)((end.Longitude - lonLabelStart) / spacing) + 1);
var labelFormat = spacing < 1d ? "{0} {1}°{2:00}'" : "{0} {1}°";
for (var lat = latLabelStart; lat <= end.Latitude; lat += spacing)
{ {
latLabels.Add(new Label(lat, new FormattedText( latLabels.Add(new Label(lat, new FormattedText(
CoordinateString(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,
@ -71,10 +63,10 @@ namespace MapControl
ParentMap.LocationToViewportPoint(new Location(lat, end.Longitude))); ParentMap.LocationToViewportPoint(new Location(lat, end.Longitude)));
} }
for (var lon = lonLabelStart; lon <= end.Longitude; lon += spacing) for (var lon = lonLabelStart; lon <= end.Longitude; lon += lineDistance)
{ {
lonLabels.Add(new Label(lon, new FormattedText( lonLabels.Add(new Label(lon, new FormattedText(
CoordinateString(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,

View file

@ -16,25 +16,56 @@ namespace MapControl
/// </summary> /// </summary>
public partial class MapGraticule : MapOverlay public partial class MapGraticule : MapOverlay
{ {
/// <summary> public static readonly DependencyProperty MinLineDistanceProperty = DependencyProperty.Register(
/// Graticule line spacings in degrees. "MinLineDistance", typeof(double), typeof(MapGraticule), new PropertyMetadata(150d));
/// </summary>
public static double[] LineSpacings =
new double[] { 1d / 60d, 1d / 30d, 1d / 12d, 1d / 6d, 1d / 4d, 1d / 3d, 1d / 2d, 1d, 2d, 5d, 10d, 15d, 20d, 30d, 45d };
public static readonly DependencyProperty MinLineSpacingProperty = DependencyProperty.Register(
"MinLineSpacing", typeof(double), typeof(MapGraticule), new PropertyMetadata(150d));
/// <summary> /// <summary>
/// Minimum spacing in pixels between adjacent graticule lines. /// Minimum graticule line distance in pixels. The default value is 150.
/// </summary> /// </summary>
public double MinLineSpacing public double MinLineDistance
{ {
get { return (double)GetValue(MinLineSpacingProperty); } get { return (double)GetValue(MinLineDistanceProperty); }
set { SetValue(MinLineSpacingProperty, value); } set { SetValue(MinLineDistanceProperty, value); }
} }
private static string CoordinateString(double value, string format, string hemispheres) private double GetLineDistance()
{
var minDistance = MinLineDistance * 360d / (Math.Pow(2d, ParentMap.ZoomLevel) * (double)TileSource.TileSize);
var scale = 1d;
if (minDistance < 1d)
{
scale = minDistance < 1d / 60d ? 3600d : 60d;
minDistance *= scale;
}
var lineDistances = new double[] { 1d, 2d, 5d, 10d, 15d, 30d, 60d };
var i = 0;
while (i < lineDistances.Length - 1 && lineDistances[i] < minDistance)
{
i++;
}
return lineDistances[i] / scale;
}
private static string GetLabelFormat(double lineDistance)
{
if (lineDistance < 1d / 60d)
{
return "{0} {1}°{2:00}'{3:00}\"";
}
if (lineDistance < 1d)
{
return "{0} {1}°{2:00}'";
}
return "{0} {1}°";
}
private static string GetLabelText(double value, string format, string hemispheres)
{ {
var hemisphere = hemispheres[0]; var hemisphere = hemispheres[0];
@ -44,9 +75,9 @@ namespace MapControl
hemisphere = hemispheres[1]; hemisphere = hemispheres[1];
} }
var minutes = (int)Math.Round(value * 60d); var seconds = (int)Math.Round(value * 3600d);
return string.Format(format, hemisphere, minutes / 60, (double)(minutes % 60)); return string.Format(format, hemisphere, seconds / 3600, (seconds / 60) % 60, seconds % 60);
} }
} }
} }

View file

@ -194,7 +194,7 @@ namespace MapControl
{ {
From = 0d, From = 0d,
To = 1d, To = 1d,
Duration = Tile.OpacityAnimationDuration, Duration = Tile.FadeDuration,
FillBehavior = FillBehavior.Stop FillBehavior = FillBehavior.Stop
}; };

View file

@ -31,8 +31,8 @@ namespace MapControl
/// <summary> /// <summary>
/// Helper method to work around missing property value inheritance in Silverlight and Windows Runtime. /// Helper method to work around missing property value inheritance in Silverlight and Windows Runtime.
/// Adds Loaded and Unloaded handlers to the specified FrameworkElement, which set and clear the value /// Adds Loaded and Unloaded event handlers to the specified FrameworkElement, which set and clear the
/// of the MapPanel.ParentMap attached property. /// value of the MapPanel.ParentMap attached property.
/// </summary> /// </summary>
public static void AddParentMapHandlers(FrameworkElement element) public static void AddParentMapHandlers(FrameworkElement element)
{ {

View file

@ -16,8 +16,8 @@ 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), new PropertyMetadata(FillRule.EvenOdd, "FillRule", typeof(FillRule), typeof(MapPolyline),
(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()
{ {

View file

@ -11,8 +11,8 @@ namespace MapControl
public partial class MapPolyline public partial class MapPolyline
{ {
public static readonly DependencyProperty FillRuleProperty = StreamGeometry.FillRuleProperty.AddOwner( public static readonly DependencyProperty FillRuleProperty = StreamGeometry.FillRuleProperty.AddOwner(
typeof(MapPolyline), new FrameworkPropertyMetadata( typeof(MapPolyline),
(o, e) => ((StreamGeometry)((MapPolyline)o).Data).FillRule = (FillRule)e.NewValue)); new FrameworkPropertyMetadata((o, e) => ((StreamGeometry)((MapPolyline)o).Data).FillRule = (FillRule)e.NewValue));
public MapPolyline() public MapPolyline()
{ {

View file

@ -23,22 +23,22 @@ namespace MapControl
static MapScale() static MapScale()
{ {
UIElement.IsHitTestVisibleProperty.OverrideMetadata( IsHitTestVisibleProperty.OverrideMetadata(
typeof(MapScale), new FrameworkPropertyMetadata(false)); typeof(MapScale), new FrameworkPropertyMetadata(false));
FrameworkElement.MinWidthProperty.OverrideMetadata( MinWidthProperty.OverrideMetadata(
typeof(MapScale), new FrameworkPropertyMetadata(100d)); typeof(MapScale), new FrameworkPropertyMetadata(100d));
FrameworkElement.HorizontalAlignmentProperty.OverrideMetadata( HorizontalAlignmentProperty.OverrideMetadata(
typeof(MapScale), new FrameworkPropertyMetadata(HorizontalAlignment.Right)); typeof(MapScale), new FrameworkPropertyMetadata(HorizontalAlignment.Right));
FrameworkElement.VerticalAlignmentProperty.OverrideMetadata( VerticalAlignmentProperty.OverrideMetadata(
typeof(MapScale), new FrameworkPropertyMetadata(VerticalAlignment.Bottom)); typeof(MapScale), new FrameworkPropertyMetadata(VerticalAlignment.Bottom));
MapOverlay.StrokeStartLineCapProperty.OverrideMetadata( StrokeStartLineCapProperty.OverrideMetadata(
typeof(MapScale), new FrameworkPropertyMetadata(PenLineCap.Round)); typeof(MapScale), new FrameworkPropertyMetadata(PenLineCap.Round));
MapOverlay.StrokeEndLineCapProperty.OverrideMetadata( StrokeEndLineCapProperty.OverrideMetadata(
typeof(MapScale), new FrameworkPropertyMetadata(PenLineCap.Round)); typeof(MapScale), new FrameworkPropertyMetadata(PenLineCap.Round));
} }

View file

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

View file

@ -21,13 +21,13 @@ namespace MapControl
{ {
public partial class Tile public partial class Tile
{ {
public void SetImage(ImageSource image, bool animateOpacity = true, bool isDownloading = true) public void SetImage(ImageSource image, bool fadeIn = true, bool isDownloading = true)
{ {
Pending = false; Pending = false;
if (image != null) if (image != null)
{ {
if (animateOpacity && OpacityAnimationDuration > TimeSpan.Zero) if (fadeIn && FadeDuration > TimeSpan.Zero)
{ {
BitmapImage bitmap; BitmapImage bitmap;
@ -39,7 +39,7 @@ namespace MapControl
else else
{ {
Image.BeginAnimation(Image.OpacityProperty, Image.BeginAnimation(Image.OpacityProperty,
new DoubleAnimation { From = 0d, To = 1d, Duration = OpacityAnimationDuration }); new DoubleAnimation { From = 0d, To = 1d, Duration = FadeDuration });
} }
} }
else else
@ -59,7 +59,7 @@ namespace MapControl
bitmap.ImageFailed -= BitmapImageFailed; bitmap.ImageFailed -= BitmapImageFailed;
Image.BeginAnimation(Image.OpacityProperty, Image.BeginAnimation(Image.OpacityProperty,
new DoubleAnimation { From = 0d, To = 1d, Duration = OpacityAnimationDuration }); new DoubleAnimation { From = 0d, To = 1d, Duration = FadeDuration });
} }
private void BitmapImageFailed(object sender, ExceptionRoutedEventArgs e) private void BitmapImageFailed(object sender, ExceptionRoutedEventArgs e)

View file

@ -12,13 +12,13 @@ namespace MapControl
{ {
public partial class Tile public partial class Tile
{ {
public void SetImage(ImageSource image, bool animateOpacity = true) public void SetImage(ImageSource image, bool fadeIn = true)
{ {
Pending = false; Pending = false;
if (image != null) if (image != null)
{ {
if (animateOpacity && OpacityAnimationDuration > TimeSpan.Zero) if (fadeIn && FadeDuration > TimeSpan.Zero)
{ {
var bitmap = image as BitmapSource; var bitmap = image as BitmapSource;
@ -30,7 +30,7 @@ namespace MapControl
else else
{ {
Image.BeginAnimation(Image.OpacityProperty, Image.BeginAnimation(Image.OpacityProperty,
new DoubleAnimation(0d, 1d, OpacityAnimationDuration)); new DoubleAnimation(0d, 1d, FadeDuration));
} }
} }
else else
@ -50,7 +50,7 @@ namespace MapControl
bitmap.DownloadFailed -= BitmapDownloadFailed; bitmap.DownloadFailed -= BitmapDownloadFailed;
Image.BeginAnimation(Image.OpacityProperty, Image.BeginAnimation(Image.OpacityProperty,
new DoubleAnimation(0d, 1d, OpacityAnimationDuration)); new DoubleAnimation(0d, 1d, FadeDuration));
} }
private void BitmapDownloadFailed(object sender, ExceptionEventArgs e) private void BitmapDownloadFailed(object sender, ExceptionEventArgs e)

View file

@ -13,7 +13,12 @@ namespace MapControl
{ {
public partial class Tile public partial class Tile
{ {
public static TimeSpan OpacityAnimationDuration = TimeSpan.FromSeconds(0.1); public static TimeSpan FadeDuration { get; set; }
static Tile()
{
FadeDuration = TimeSpan.FromSeconds(0.2);
}
public readonly int ZoomLevel; public readonly int ZoomLevel;
public readonly int X; public readonly int X;

46
MapControl/TileGrid.cs Normal file
View file

@ -0,0 +1,46 @@
// XAML Map Control - http://xamlmapcontrol.codeplex.com/
// © 2015 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL)
using System;
namespace MapControl
{
public class TileGrid : IEquatable<TileGrid>
{
public readonly int ZoomLevel;
public readonly int XMin;
public readonly int YMin;
public readonly int XMax;
public readonly int YMax;
public TileGrid(int zoomLevel, int xMin, int yMin, int xMax, int yMax)
{
ZoomLevel = zoomLevel;
XMin = xMin;
YMin = yMin;
XMax = xMax;
YMax = yMax;
}
public bool Equals(TileGrid tileGrid)
{
return tileGrid != null
&& tileGrid.ZoomLevel == ZoomLevel
&& tileGrid.XMin == XMin
&& tileGrid.YMin == YMin
&& tileGrid.XMax == XMax
&& tileGrid.YMax == YMax;
}
public override bool Equals(object obj)
{
return Equals(obj as TileGrid);
}
public override int GetHashCode()
{
return ZoomLevel ^ XMin ^ YMin ^ XMax ^ YMax;
}
}
}

View file

@ -40,17 +40,23 @@ namespace MapControl
/// was transmitted on download. The default and recommended minimum value is seven days. /// was transmitted on download. The default and recommended minimum value is seven days.
/// See OpenStreetMap tile usage policy: http://wiki.openstreetmap.org/wiki/Tile_usage_policy /// See OpenStreetMap tile usage policy: http://wiki.openstreetmap.org/wiki/Tile_usage_policy
/// </summary> /// </summary>
public static TimeSpan DefaultCacheExpiration = TimeSpan.FromDays(7); public static TimeSpan DefaultCacheExpiration { get; set; }
/// <summary> /// <summary>
/// The ObjectCache used to cache tile images. The default is MemoryCache.Default. /// The ObjectCache used to cache tile images. The default is MemoryCache.Default.
/// </summary> /// </summary>
public static ObjectCache Cache = MemoryCache.Default; public static ObjectCache Cache { get; set; }
/// <summary> /// <summary>
/// Optional value to be used for the HttpWebRequest.UserAgent property. The default is null. /// Optional value to be used for the HttpWebRequest.UserAgent property. The default is null.
/// </summary> /// </summary>
public static string HttpUserAgent; public static string HttpUserAgent { get; set; }
static TileImageLoader()
{
DefaultCacheExpiration = TimeSpan.FromDays(7);
Cache = MemoryCache.Default;
}
private class PendingTile private class PendingTile
{ {

View file

@ -38,13 +38,18 @@ namespace MapControl
/// was transmitted on download. The default and recommended minimum value is seven days. /// was transmitted on download. The default and recommended minimum value is seven days.
/// See OpenStreetMap tile usage policy: http://wiki.openstreetmap.org/wiki/Tile_usage_policy /// See OpenStreetMap tile usage policy: http://wiki.openstreetmap.org/wiki/Tile_usage_policy
/// </summary> /// </summary>
public static TimeSpan DefaultCacheExpiration = TimeSpan.FromDays(7); public static TimeSpan DefaultCacheExpiration { get; set; }
/// <summary> /// <summary>
/// The IImageCache implementation used to cache tile images. The default is null. /// The IImageCache implementation used to cache tile images. The default is null.
/// </summary> /// </summary>
public static Caching.IImageCache Cache; public static Caching.IImageCache Cache;
static TileImageLoader()
{
DefaultCacheExpiration = TimeSpan.FromDays(7);
}
private class PendingTile private class PendingTile
{ {
public readonly Tile Tile; public readonly Tile Tile;

View file

@ -4,8 +4,10 @@
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
@ -20,24 +22,28 @@ namespace MapControl
MapPanel.AddParentMapHandlers(this); MapPanel.AddParentMapHandlers(this);
} }
private Matrix GetTileIndexMatrix(int zoomLevel) private Rect GetTileIndexBounds(int zoomLevel)
{ {
var scale = (double)(1 << zoomLevel) / 360d; var scale = (double)(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 parentMap.ViewportTransform.Matrix return transform.TransformBounds(new Rect(new Point(), parentMap.RenderSize));
.Invert() // view to map coordinates
.Translate(180d, -180d)
.Scale(scale, -scale); // map coordinates to tile indices
} }
private void SetRenderTransform() private void SetRenderTransform()
{ {
var scale = Math.Pow(2d, parentMap.ZoomLevel - TileZoomLevel); var scale = Math.Pow(2d, parentMap.ZoomLevel - TileGrid.ZoomLevel);
var offsetX = parentMap.ViewportOrigin.X - (180d + parentMap.MapOrigin.X) * parentMap.ViewportScale; var offsetX = parentMap.ViewportOrigin.X - (180d + parentMap.MapOrigin.X) * parentMap.ViewportScale;
var offsetY = parentMap.ViewportOrigin.Y - (180d - parentMap.MapOrigin.Y) * parentMap.ViewportScale; var offsetY = parentMap.ViewportOrigin.Y - (180d - parentMap.MapOrigin.Y) * parentMap.ViewportScale;
((MatrixTransform)RenderTransform).Matrix = ((MatrixTransform)RenderTransform).Matrix =
new Matrix(1d, 0d, 0d, 1d, TileRect.X * TileSource.TileSize, TileRect.Y * TileSource.TileSize) new Matrix(1d, 0d, 0d, 1d, TileSource.TileSize * TileGrid.XMin, TileSource.TileSize * TileGrid.YMin)
.Scale(scale, scale) .Scale(scale, scale)
.Translate(offsetX, offsetY) .Translate(offsetX, offsetY)
.RotateAt(parentMap.Heading, parentMap.ViewportOrigin.X, parentMap.ViewportOrigin.Y); ; .RotateAt(parentMap.Heading, parentMap.ViewportOrigin.X, parentMap.ViewportOrigin.Y); ;

View file

@ -12,11 +12,11 @@ namespace MapControl
{ {
static TileLayer() static TileLayer()
{ {
UIElement.IsHitTestVisibleProperty.OverrideMetadata( IsHitTestVisibleProperty.OverrideMetadata(
typeof(TileLayer), new FrameworkPropertyMetadata(false)); typeof(TileLayer), new FrameworkPropertyMetadata(false));
} }
private Matrix GetTileIndexMatrix(int zoomLevel) private Rect GetTileIndexBounds(int zoomLevel)
{ {
var scale = (double)(1 << zoomLevel) / 360d; var scale = (double)(1 << zoomLevel) / 360d;
var transform = parentMap.ViewportTransform.Matrix; var transform = parentMap.ViewportTransform.Matrix;
@ -25,16 +25,16 @@ namespace MapControl
transform.Translate(180d, -180d); transform.Translate(180d, -180d);
transform.Scale(scale, -scale); // map coordinates to tile indices transform.Scale(scale, -scale); // map coordinates to tile indices
return transform; return new MatrixTransform(transform).TransformBounds(new Rect(parentMap.RenderSize));
} }
private void SetRenderTransform() private void SetRenderTransform()
{ {
var scale = Math.Pow(2d, parentMap.ZoomLevel - TileZoomLevel); var scale = Math.Pow(2d, parentMap.ZoomLevel - TileGrid.ZoomLevel);
var offsetX = parentMap.ViewportOrigin.X - (180d + parentMap.MapOrigin.X) * parentMap.ViewportScale; var offsetX = parentMap.ViewportOrigin.X - (180d + parentMap.MapOrigin.X) * parentMap.ViewportScale;
var offsetY = parentMap.ViewportOrigin.Y - (180d - parentMap.MapOrigin.Y) * parentMap.ViewportScale; var offsetY = parentMap.ViewportOrigin.Y - (180d - parentMap.MapOrigin.Y) * parentMap.ViewportScale;
var transform = new Matrix(1d, 0d, 0d, 1d, TileRect.X * TileSource.TileSize, TileRect.Y * TileSource.TileSize); var transform = new Matrix(1d, 0d, 0d, 1d, TileSource.TileSize * TileGrid.XMin, TileSource.TileSize * TileGrid.YMin);
transform.Scale(scale, scale); transform.Scale(scale, scale);
transform.Translate(offsetX, offsetY); transform.Translate(offsetX, offsetY);
transform.RotateAt(parentMap.Heading, parentMap.ViewportOrigin.X, parentMap.ViewportOrigin.Y); transform.RotateAt(parentMap.Heading, parentMap.ViewportOrigin.X, parentMap.ViewportOrigin.Y);

View file

@ -15,7 +15,6 @@ using System.Windows;
using System.Windows.Markup; using System.Windows.Markup;
using System.Windows.Media; using System.Windows.Media;
using System.Windows.Threading; using System.Windows.Threading;
using System.Diagnostics;
#endif #endif
namespace MapControl namespace MapControl
@ -58,7 +57,7 @@ namespace MapControl
public static readonly DependencyProperty ZoomLevelOffsetProperty = DependencyProperty.Register( public static readonly DependencyProperty ZoomLevelOffsetProperty = DependencyProperty.Register(
"ZoomLevelOffset", typeof(double), typeof(TileLayer), "ZoomLevelOffset", typeof(double), typeof(TileLayer),
new PropertyMetadata(0d, (o, e) => ((TileLayer)o).UpdateTileRect())); new PropertyMetadata(0d, (o, e) => ((TileLayer)o).UpdateTileGrid()));
public static readonly DependencyProperty MinZoomLevelProperty = DependencyProperty.Register( public static readonly DependencyProperty MinZoomLevelProperty = DependencyProperty.Register(
"MinZoomLevel", typeof(int), typeof(TileLayer), new PropertyMetadata(0)); "MinZoomLevel", typeof(int), typeof(TileLayer), new PropertyMetadata(0));
@ -98,18 +97,16 @@ namespace MapControl
RenderTransform = new MatrixTransform(); RenderTransform = new MatrixTransform();
TileImageLoader = tileImageLoader; TileImageLoader = tileImageLoader;
Tiles = new List<Tile>(); Tiles = new List<Tile>();
TileZoomLevel = -1;
updateTimer = new DispatcherTimer { Interval = UpdateInterval }; updateTimer = new DispatcherTimer { Interval = UpdateInterval };
updateTimer.Tick += (s, e) => UpdateTileRect(); updateTimer.Tick += (s, e) => UpdateTileGrid();
} }
partial void Initialize(); // Windows Runtime and Silverlight only partial void Initialize(); // Windows Runtime and Silverlight only
public ITileImageLoader TileImageLoader { get; private set; } public ITileImageLoader TileImageLoader { get; private set; }
public ICollection<Tile> Tiles { get; private set; } public ICollection<Tile> Tiles { get; private set; }
public Int32Rect TileRect { get; private set; } public TileGrid TileGrid { get; private set; }
public int TileZoomLevel { get; private set; }
/// <summary> /// <summary>
/// Provides map tile URIs or images. /// Provides map tile URIs or images.
@ -228,8 +225,6 @@ namespace MapControl
if (parentMap != null) if (parentMap != null)
{ {
parentMap.ViewportChanged -= ViewportChanged; parentMap.ViewportChanged -= ViewportChanged;
TileZoomLevel = -1;
UpdateTiles(true);
} }
parentMap = value; parentMap = value;
@ -237,64 +232,62 @@ namespace MapControl
if (parentMap != null) if (parentMap != null)
{ {
parentMap.ViewportChanged += ViewportChanged; parentMap.ViewportChanged += ViewportChanged;
ViewportChanged(this, EventArgs.Empty); mapOriginX = parentMap.MapOrigin.X;
} }
UpdateTileGrid();
} }
} }
private void ViewportChanged(object sender, EventArgs e) private void ViewportChanged(object sender, EventArgs e)
{ {
if (TileZoomLevel < 0 || Math.Abs(parentMap.MapOrigin.X - mapOriginX) > 180d) if (TileGrid == null || Math.Abs(parentMap.MapOrigin.X - mapOriginX) > 180d)
{ {
// immediately handle map origin leap when map center moves across 180° longitude // immediately handle map origin leap when map center moves across 180° longitude
UpdateTileRect(); UpdateTileGrid();
} }
else else
{ {
SetRenderTransform(); SetRenderTransform();
if (!UpdateWhileViewportChanging) if (updateTimer.IsEnabled && !UpdateWhileViewportChanging)
{ {
updateTimer.Stop(); updateTimer.Stop(); // restart
} }
updateTimer.Start(); if (!updateTimer.IsEnabled)
{
updateTimer.Start();
}
} }
mapOriginX = parentMap.MapOrigin.X; mapOriginX = parentMap.MapOrigin.X;
} }
protected void UpdateTileRect() protected void UpdateTileGrid()
{ {
updateTimer.Stop(); updateTimer.Stop();
if (parentMap != null) if (parentMap != null)
{ {
var zoomLevel = (int)Math.Round(parentMap.ZoomLevel + ZoomLevelOffset); var zoomLevel = Math.Max(0, (int)Math.Round(parentMap.ZoomLevel + ZoomLevelOffset));
var transform = GetTileIndexMatrix(zoomLevel); 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));
// tile indices of visible rectangle if (!tileGrid.Equals(TileGrid))
var p1 = transform.Transform(new Point(0d, 0d));
var p2 = transform.Transform(new Point(parentMap.RenderSize.Width, 0d));
var p3 = transform.Transform(new Point(0d, parentMap.RenderSize.Height));
var p4 = transform.Transform(new Point(parentMap.RenderSize.Width, parentMap.RenderSize.Height));
// index ranges of visible tiles
var x1 = (int)Math.Floor(Math.Min(p1.X, Math.Min(p2.X, Math.Min(p3.X, p4.X))));
var y1 = (int)Math.Floor(Math.Min(p1.Y, Math.Min(p2.Y, Math.Min(p3.Y, p4.Y))));
var x2 = (int)Math.Floor(Math.Max(p1.X, Math.Max(p2.X, Math.Max(p3.X, p4.X))));
var y2 = (int)Math.Floor(Math.Max(p1.Y, Math.Max(p2.Y, Math.Max(p3.Y, p4.Y))));
var rect = new Int32Rect(x1, y1, x2 - x1 + 1, y2 - y1 + 1);
if (TileZoomLevel != zoomLevel || TileRect != rect)
{ {
TileZoomLevel = zoomLevel; TileGrid = tileGrid;
TileRect = rect;
SetRenderTransform(); SetRenderTransform();
UpdateTiles(false); UpdateTiles(false);
} }
} }
else
{
TileGrid = null;
UpdateTiles(true);
}
} }
protected virtual void UpdateTiles(bool clearTiles) protected virtual void UpdateTiles(bool clearTiles)
@ -328,9 +321,9 @@ namespace MapControl
{ {
var newTiles = new List<Tile>(); var newTiles = new List<Tile>();
if (TileZoomLevel >= 0 && parentMap != null && TileSource != null) if (TileGrid != null && parentMap != null && TileSource != null)
{ {
var maxZoomLevel = Math.Min(TileZoomLevel, 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.TileLayers.FirstOrDefault())
@ -341,11 +334,11 @@ namespace MapControl
for (var z = minZoomLevel; z <= maxZoomLevel; z++) for (var z = minZoomLevel; z <= maxZoomLevel; z++)
{ {
var tileSize = 1 << (TileZoomLevel - z); var tileSize = 1 << (TileGrid.ZoomLevel - z);
var x1 = (int)Math.Floor((double)TileRect.X / tileSize); // may be negative var x1 = (int)Math.Floor((double)TileGrid.XMin / tileSize); // may be negative
var x2 = (TileRect.X + TileRect.Width - 1) / tileSize; var x2 = TileGrid.XMax / tileSize;
var y1 = Math.Max(TileRect.Y / tileSize, 0); var y1 = Math.Max(TileGrid.YMin / tileSize, 0);
var y2 = Math.Min((TileRect.Y + TileRect.Height - 1) / tileSize, (1 << z) - 1); var y2 = Math.Min(TileGrid.YMax / tileSize, (1 << z) - 1);
for (var y = y1; y <= y2; y++) for (var y = y1; y <= y2; y++)
{ {
@ -378,13 +371,13 @@ namespace MapControl
protected override Size ArrangeOverride(Size finalSize) protected override Size ArrangeOverride(Size finalSize)
{ {
if (TileZoomLevel >= 0) if (TileGrid != null)
{ {
foreach (var tile in Tiles) foreach (var tile in Tiles)
{ {
var tileSize = (double)(256 << (TileZoomLevel - tile.ZoomLevel)); var tileSize = TileSource.TileSize << (TileGrid.ZoomLevel - tile.ZoomLevel);
var x = tileSize * tile.X - 256 * TileRect.X; var x = tileSize * tile.X - TileSource.TileSize * TileGrid.XMin;
var y = tileSize * tile.Y - 256 * TileRect.Y; var y = tileSize * tile.Y - TileSource.TileSize * TileGrid.YMin;
tile.Image.Width = tileSize; tile.Image.Width = tileSize;
tile.Image.Height = tileSize; tile.Image.Height = tileSize;

View file

@ -12,7 +12,7 @@ namespace MapControl
/// </summary> /// </summary>
public partial class TileSource public partial class TileSource
{ {
public const double TileSize = 256; public const int TileSize = 256;
public const double MetersPerDegree = 6378137d * Math.PI / 180d; // WGS 84 semi major axis 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;
@ -165,15 +165,14 @@ namespace MapControl
var east = MetersPerDegree * ((double)(x + 1) * tileSize - 180d); var east = MetersPerDegree * ((double)(x + 1) * tileSize - 180d);
var south = MetersPerDegree * (180d - (double)(y + 1) * tileSize); var south = MetersPerDegree * (180d - (double)(y + 1) * tileSize);
var north = MetersPerDegree * (180d - (double)y * tileSize); var north = MetersPerDegree * (180d - (double)y * tileSize);
var imageSize = TileSize.ToString("F0");
return new Uri(uriFormat return new Uri(uriFormat
.Replace("{W}", west.ToString(CultureInfo.InvariantCulture)) .Replace("{W}", west.ToString(CultureInfo.InvariantCulture))
.Replace("{S}", south.ToString(CultureInfo.InvariantCulture)) .Replace("{S}", south.ToString(CultureInfo.InvariantCulture))
.Replace("{E}", east.ToString(CultureInfo.InvariantCulture)) .Replace("{E}", east.ToString(CultureInfo.InvariantCulture))
.Replace("{N}", north.ToString(CultureInfo.InvariantCulture)) .Replace("{N}", north.ToString(CultureInfo.InvariantCulture))
.Replace("{X}", imageSize) .Replace("{X}", TileSize.ToString())
.Replace("{Y}", imageSize)); .Replace("{Y}", TileSize.ToString()));
} }
private Uri GetLatLonBoundingBoxUri(int x, int y, int zoomLevel) private Uri GetLatLonBoundingBoxUri(int x, int y, int zoomLevel)
@ -183,15 +182,14 @@ namespace MapControl
var east = (double)(x + 1) * tileSize - 180d; var east = (double)(x + 1) * tileSize - 180d;
var south = MercatorTransform.YToLatitude(180d - (double)(y + 1) * tileSize); var south = MercatorTransform.YToLatitude(180d - (double)(y + 1) * tileSize);
var north = MercatorTransform.YToLatitude(180d - (double)y * tileSize); var north = MercatorTransform.YToLatitude(180d - (double)y * tileSize);
var imageSize = TileSize.ToString("F0");
return new Uri(uriFormat return new Uri(uriFormat
.Replace("{w}", west.ToString(CultureInfo.InvariantCulture)) .Replace("{w}", west.ToString(CultureInfo.InvariantCulture))
.Replace("{s}", south.ToString(CultureInfo.InvariantCulture)) .Replace("{s}", south.ToString(CultureInfo.InvariantCulture))
.Replace("{e}", east.ToString(CultureInfo.InvariantCulture)) .Replace("{e}", east.ToString(CultureInfo.InvariantCulture))
.Replace("{n}", north.ToString(CultureInfo.InvariantCulture)) .Replace("{n}", north.ToString(CultureInfo.InvariantCulture))
.Replace("{X}", imageSize) .Replace("{X}", TileSize.ToString())
.Replace("{Y}", imageSize)); .Replace("{Y}", TileSize.ToString()));
} }
} }
} }

View file

@ -57,9 +57,6 @@
<Compile Include="..\IMapElement.cs"> <Compile Include="..\IMapElement.cs">
<Link>IMapElement.cs</Link> <Link>IMapElement.cs</Link>
</Compile> </Compile>
<Compile Include="..\Int32Rect.cs">
<Link>Int32Rect.cs</Link>
</Compile>
<Compile Include="..\ITileImageLoader.cs"> <Compile Include="..\ITileImageLoader.cs">
<Link>ITileImageLoader.cs</Link> <Link>ITileImageLoader.cs</Link>
</Compile> </Compile>
@ -147,6 +144,9 @@
<Compile Include="..\Tile.Silverlight.WinRT.cs"> <Compile Include="..\Tile.Silverlight.WinRT.cs">
<Link>Tile.Silverlight.WinRT.cs</Link> <Link>Tile.Silverlight.WinRT.cs</Link>
</Compile> </Compile>
<Compile Include="..\TileGrid.cs">
<Link>TileGrid.cs</Link>
</Compile>
<Compile Include="..\TileImageLoader.WinRT.cs"> <Compile Include="..\TileImageLoader.WinRT.cs">
<Link>TileImageLoader.WinRT.cs</Link> <Link>TileImageLoader.WinRT.cs</Link>
</Compile> </Compile>

View file

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

View file

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

View file

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

View file

@ -35,7 +35,7 @@
Foreground="White" Background="Black"/> Foreground="White" Background="Black"/>
<map:TileLayer SourceName="MapQuest OpenStreetMap" <map:TileLayer SourceName="MapQuest OpenStreetMap"
Description="Maps © [MapQuest](http://www.mapquest.com/), Data © [OpenStreetMap Contributors](http://www.openstreetmap.org/copyright)" Description="Maps © [MapQuest](http://www.mapquest.com/), Data © [OpenStreetMap Contributors](http://www.openstreetmap.org/copyright)"
TileSource="http://otile{n}.mqcdn.com/tiles/1.0.0/osm/{z}/{x}/{y}.png" TileSource="http://otile{n}.mqcdn.com/tiles/1.0.0/osm/{z}/{x}/{y}.jpg"
MaxZoomLevel="19"/> MaxZoomLevel="19"/>
<map:TileLayer SourceName="Seamarks" <map:TileLayer SourceName="Seamarks"
TileSource="http://tiles.openseamap.org/seamark/{z}/{x}/{y}.png" TileSource="http://tiles.openseamap.org/seamark/{z}/{x}/{y}.png"
@ -140,7 +140,7 @@
<RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/>
</Grid.RowDefinitions> </Grid.RowDefinitions>
<map:Map x:Name="map" TileLayer="{Binding [OpenStreetMap], Source={StaticResource TileLayers}}" <map:Map x:Name="map" TileLayer="{Binding [OpenStreetMap], Source={StaticResource TileLayers}}"
Center="{Binding MapCenter}" MinZoomLevel="2" MaxZoomLevel="18" ZoomLevel="11" Center="{Binding MapCenter}" MinZoomLevel="2" ZoomLevel="11"
MouseMove="MapMouseMove" MouseLeave="MapMouseLeave"> MouseMove="MapMouseMove" MouseLeave="MapMouseLeave">
<map:MapImage x:Name="mapImage" South="53.54031" North="53.74871" West="8.08594" East="8.43750" <map:MapImage x:Name="mapImage" South="53.54031" North="53.74871" West="8.08594" East="8.43750"

View file

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

View file

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

View file

@ -39,7 +39,7 @@
</map:TileLayer> </map:TileLayer>
<map:TileLayer SourceName="MapQuest OpenStreetMap" <map:TileLayer SourceName="MapQuest OpenStreetMap"
Description="Maps © [MapQuest](http://www.mapquest.com/), Data © [OpenStreetMap Contributors](http://www.openstreetmap.org/copyright)"> Description="Maps © [MapQuest](http://www.mapquest.com/), Data © [OpenStreetMap Contributors](http://www.openstreetmap.org/copyright)">
<map:TileSource UriFormat="http://otile{n}.mqcdn.com/tiles/1.0.0/osm/{z}/{x}/{y}.png"/> <map:TileSource UriFormat="http://otile{n}.mqcdn.com/tiles/1.0.0/osm/{z}/{x}/{y}.jpg"/>
</map:TileLayer> </map:TileLayer>
<map:TileLayer SourceName="Seamarks" Description="© OpenSeaMap Contributors" <map:TileLayer SourceName="Seamarks" Description="© OpenSeaMap Contributors"
MinZoomLevel="10" MaxZoomLevel="18"> MinZoomLevel="10" MaxZoomLevel="18">
@ -167,7 +167,7 @@
<RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/>
</Grid.RowDefinitions> </Grid.RowDefinitions>
<map:Map x:Name="map" TileLayer="{Binding [OpenStreetMap], Source={StaticResource TileLayers}}" <map:Map x:Name="map" TileLayer="{Binding [OpenStreetMap], Source={StaticResource TileLayers}}"
Center="{Binding MapCenter}" MinZoomLevel="2" MaxZoomLevel="18" ZoomLevel="11" ManipulationMode="All"> Center="{Binding MapCenter}" MinZoomLevel="2" ZoomLevel="11" ManipulationMode="All">
<map:MapImage x:Name="mapImage" South="53.54031" North="53.74871" West="8.08594" East="8.43750" <map:MapImage x:Name="mapImage" South="53.54031" North="53.74871" West="8.08594" East="8.43750"
Source="10_535_330.jpg" Opacity="0.5"/> Source="10_535_330.jpg" Opacity="0.5"/>
<map:MapGraticule Opacity="0.6"/> <map:MapGraticule Opacity="0.6"/>
@ -194,7 +194,7 @@
</Path.Data> </Path.Data>
</Path> </Path>
<map:MapPath Fill="Aqua" Opacity="0.5"> <map:MapPath Stroke="Blue" Fill="Aqua" Opacity="0.5">
<map:MapPath.Data> <map:MapPath.Data>
<GeometryGroup FillRule="EvenOdd"> <GeometryGroup FillRule="EvenOdd">
<EllipseGeometry Center="8.2,63.5" RadiusX="0.025" RadiusY="0.025"/> <EllipseGeometry Center="8.2,63.5" RadiusX="0.025" RadiusY="0.025"/>

View file

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

View file

@ -33,7 +33,7 @@
Foreground="White" Background="Black"/> Foreground="White" Background="Black"/>
<map:TileLayer x:Key="MapQuest" SourceName="MapQuest OpenStreetMap" <map:TileLayer x:Key="MapQuest" SourceName="MapQuest OpenStreetMap"
Description="Maps © [MapQuest](http://www.mapquest.com/), Data © [OpenStreetMap Contributors](http://www.openstreetmap.org/copyright)" Description="Maps © [MapQuest](http://www.mapquest.com/), Data © [OpenStreetMap Contributors](http://www.openstreetmap.org/copyright)"
TileSource="http://otile{n}.mqcdn.com/tiles/1.0.0/osm/{z}/{x}/{y}.png" TileSource="http://otile{n}.mqcdn.com/tiles/1.0.0/osm/{z}/{x}/{y}.jpg"
MaxZoomLevel="19"/> MaxZoomLevel="19"/>
<map:TileLayer x:Key="Seamarks" SourceName="Seamarks" <map:TileLayer x:Key="Seamarks" SourceName="Seamarks"
TileSource="http://tiles.openseamap.org/seamark/{z}/{x}/{y}.png" TileSource="http://tiles.openseamap.org/seamark/{z}/{x}/{y}.png"

View file

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