Version 1.9.0:

- added MapBase.ZoomToBounds method
- fixed coercing property values in MapBase
- improved Location property handling in MapPanel to keep viewport positions near map center
- use common view model in sample applications
This commit is contained in:
ClemensF 2013-11-17 16:52:03 +01:00
parent 9f4ab0f3e3
commit 5eafa751f8
37 changed files with 391 additions and 571 deletions

View file

@ -405,6 +405,27 @@ namespace MapControl
}
}
/// <summary>
/// Sets the TargetZoomLevel and TargetCenter properties such that the specified bounding box
/// fits into the current viewport. The TargetHeading property is set to zero.
/// </summary>
public void ZoomToBounds(Location southWest, Location northEast)
{
if (southWest.Latitude < northEast.Latitude && southWest.Longitude < northEast.Longitude)
{
var p1 = MapTransform.Transform(southWest);
var p2 = MapTransform.Transform(northEast);
var lonScale = ActualWidth / (p2.X - p1.X) * 360d / TileSource.TileSize;
var latScale = ActualHeight / (p2.Y - p1.Y) * 360d / TileSource.TileSize;
var lonZoom = Math.Log(lonScale, 2d);
var latZoom = Math.Log(latScale, 2d);
TargetZoomLevel = Math.Min(lonZoom, latZoom);
TargetCenter = MapTransform.Transform(new Point((p1.X + p2.X) / 2d, (p1.Y + p2.Y) / 2d));
TargetHeading = 0d;
}
}
protected override void OnViewportChanged()
{
base.OnViewportChanged();
@ -551,8 +572,14 @@ namespace MapControl
internalPropertyChange = false;
}
private bool CoerceLocation(Location location, double latitudeEpsilon = 0d)
private bool CoerceLocation(ref Location location, double latitudeEpsilon = 0d)
{
if (location == null)
{
location = new Location();
return true;
}
var maxLatitude = mapTransform.MaxLatitude + latitudeEpsilon;
var latitude = Math.Min(Math.Max(location.Latitude, -maxLatitude), maxLatitude);
var longitude = Location.NormalizeLongitude(location.Longitude);
@ -567,9 +594,9 @@ namespace MapControl
return false;
}
private void CoerceCenterProperty(DependencyProperty property, Location center)
private void CoerceCenterProperty(DependencyProperty property, ref Location center)
{
if (CoerceLocation(center))
if (CoerceLocation(ref center))
{
InternalSetValue(property, center);
}
@ -579,7 +606,7 @@ namespace MapControl
{
if (!internalPropertyChange)
{
CoerceCenterProperty(CenterProperty, center);
CoerceCenterProperty(CenterProperty, ref center);
ResetTransformOrigin();
UpdateTransform();
@ -595,9 +622,9 @@ namespace MapControl
{
if (!internalPropertyChange)
{
CoerceCenterProperty(TargetCenterProperty, targetCenter);
CoerceCenterProperty(TargetCenterProperty, ref targetCenter);
if (!targetCenter.Equals(Center))
if (targetCenter.Latitude != Center.Latitude || targetCenter.Longitude != Center.Longitude)
{
if (centerAnimation != null)
{
@ -652,9 +679,11 @@ namespace MapControl
if (coercedValue != minZoomLevel)
{
InternalSetValue(MinZoomLevelProperty, coercedValue);
minZoomLevel = coercedValue;
InternalSetValue(MinZoomLevelProperty, minZoomLevel);
}
else if (ZoomLevel < minZoomLevel)
if (ZoomLevel < minZoomLevel)
{
ZoomLevel = minZoomLevel;
}
@ -666,32 +695,32 @@ namespace MapControl
if (coercedValue != maxZoomLevel)
{
InternalSetValue(MaxZoomLevelProperty, coercedValue);
maxZoomLevel = coercedValue;
InternalSetValue(MaxZoomLevelProperty, maxZoomLevel);
}
else if (ZoomLevel > maxZoomLevel)
if (ZoomLevel > maxZoomLevel)
{
ZoomLevel = maxZoomLevel;
}
}
private bool CoerceZoomLevelProperty(DependencyProperty property, ref double zoomLevel)
private void CoerceZoomLevelProperty(DependencyProperty property, ref double zoomLevel)
{
var coercedValue = Math.Min(Math.Max(zoomLevel, MinZoomLevel), MaxZoomLevel);
if (coercedValue != zoomLevel)
{
InternalSetValue(property, coercedValue);
return true;
zoomLevel = coercedValue;
InternalSetValue(property, zoomLevel);
}
return false;
}
private void ZoomLevelPropertyChanged(double zoomLevel)
{
if (!internalPropertyChange &&
!CoerceZoomLevelProperty(ZoomLevelProperty, ref zoomLevel))
if (!internalPropertyChange)
{
CoerceZoomLevelProperty(ZoomLevelProperty, ref zoomLevel);
UpdateTransform();
if (zoomLevelAnimation == null)
@ -703,25 +732,28 @@ namespace MapControl
private void TargetZoomLevelPropertyChanged(double targetZoomLevel)
{
if (!internalPropertyChange &&
!CoerceZoomLevelProperty(TargetZoomLevelProperty, ref targetZoomLevel) &&
targetZoomLevel != ZoomLevel)
if (!internalPropertyChange)
{
if (zoomLevelAnimation != null)
CoerceZoomLevelProperty(TargetZoomLevelProperty, ref targetZoomLevel);
if (targetZoomLevel != ZoomLevel)
{
zoomLevelAnimation.Completed -= ZoomLevelAnimationCompleted;
if (zoomLevelAnimation != null)
{
zoomLevelAnimation.Completed -= ZoomLevelAnimationCompleted;
}
zoomLevelAnimation = new DoubleAnimation
{
To = targetZoomLevel,
Duration = AnimationDuration,
EasingFunction = AnimationEasingFunction,
FillBehavior = FillBehavior.HoldEnd
};
zoomLevelAnimation.Completed += ZoomLevelAnimationCompleted;
this.BeginAnimation(ZoomLevelProperty, zoomLevelAnimation);
}
zoomLevelAnimation = new DoubleAnimation
{
To = targetZoomLevel,
Duration = AnimationDuration,
EasingFunction = AnimationEasingFunction,
FillBehavior = FillBehavior.HoldEnd
};
zoomLevelAnimation.Completed += ZoomLevelAnimationCompleted;
this.BeginAnimation(ZoomLevelProperty, zoomLevelAnimation);
}
}
@ -742,12 +774,12 @@ namespace MapControl
private void CoerceHeadingProperty(DependencyProperty property, ref double heading)
{
var coercedValue = (heading >= -180d && heading <= 360d) ?
heading : (((heading % 360d) + 360d) % 360d);
var coercedValue = (heading >= -180d && heading <= 360d) ? heading : (((heading % 360d) + 360d) % 360d);
if (coercedValue != heading)
{
InternalSetValue(property, coercedValue);
heading = coercedValue;
InternalSetValue(property, heading);
}
}
@ -826,7 +858,7 @@ namespace MapControl
{
center = ViewportPointToLocation(new Point(RenderSize.Width / 2d, RenderSize.Height / 2d));
var coerced = CoerceLocation(center, 1e-3);
var coerced = CoerceLocation(ref center, 1e-3);
InternalSetValue(CenterProperty, center);

View file

@ -226,7 +226,6 @@ namespace MapControl
{
for (var lon = labelsStart.Longitude; lon <= end.Longitude; lon += spacing)
{
var location = new Location(lat, lon);
TextBlock label;
if (childIndex < Children.Count)
@ -273,13 +272,17 @@ namespace MapControl
{
transformGroup.Children.Add(new TranslateTransform());
transformGroup.Children.Add(ParentMap.RotateTransform);
transformGroup.Children.Add(new TranslateTransform());
}
var translateTransform = (TranslateTransform)transformGroup.Children[0];
translateTransform.X = StrokeThickness / 2d + 2d;
translateTransform.Y = -label.DesiredSize.Height / 2d;
MapPanel.SetLocation(label, location);
var viewportPosition = ParentMap.LocationToViewportPoint(new Location(lat, lon));
translateTransform = (TranslateTransform)transformGroup.Children[2];
translateTransform.X = viewportPosition.X;
translateTransform.Y = viewportPosition.Y;
}
}

View file

@ -151,6 +151,23 @@ namespace MapControl
if (parentMap != null && location != null)
{
var longitude = Location.NormalizeLongitude(location.Longitude);
var centerDistance = longitude - parentMap.Center.Longitude;
if (centerDistance > 180d)
{
longitude -= 360d;
}
else if (centerDistance < -180d)
{
longitude += 360d;
}
if (location.Longitude != longitude) // keep viewport position near map center
{
location = new Location(location.Latitude, longitude);
}
viewportPosition = parentMap.LocationToViewportPoint(location);
element.SetValue(ViewportPositionProperty, viewportPosition);
}

View file

@ -15,8 +15,8 @@ using System.Windows;
[assembly: AssemblyCompany("Clemens Fischer")]
[assembly: AssemblyCopyright("Copyright © Clemens Fischer 2012-2013")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyVersion("1.8.0")]
[assembly: AssemblyFileVersion("1.8.0")]
[assembly: AssemblyVersion("1.9.0")]
[assembly: AssemblyFileVersion("1.9.0")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCulture("")]
[assembly: ComVisible(false)]

View file

@ -17,8 +17,10 @@ namespace MapControl
{
internal partial class TileContainer : Panel
{
private Matrix GetViewportTransformMatrix(Matrix transform)
private Matrix GetViewportTransformMatrix(double scale, double offsetX, double offsetY)
{
var transform = new Matrix(scale, 0d, 0d, -scale, offsetX, offsetY);
return transform.RotateAt(rotation, viewportOrigin.X, viewportOrigin.Y);
}

View file

@ -9,8 +9,10 @@ namespace MapControl
{
internal partial class TileContainer : ContainerVisual
{
private Matrix GetViewportTransformMatrix(Matrix transform)
private Matrix GetViewportTransformMatrix(double scale, double offsetX, double offsetY)
{
var transform = new Matrix(scale, 0d, 0d, -scale, offsetX, offsetY);
transform.RotateAt(rotation, viewportOrigin.X, viewportOrigin.Y);
return transform;

View file

@ -101,7 +101,7 @@ namespace MapControl
tileLayerOffset.X = transformOffsetX - 180d * scale;
tileLayerOffset.Y = transformOffsetY - 180d * scale;
ViewportTransform.Matrix = GetViewportTransformMatrix(new Matrix(scale, 0d, 0d, -scale, transformOffsetX, transformOffsetY));
ViewportTransform.Matrix = GetViewportTransformMatrix(scale, transformOffsetX, transformOffsetY);
if (Math.Sign(mapOrigin.X) != Math.Sign(oldMapOriginX) && Math.Abs(mapOrigin.X) > 90d)
{

View file

@ -104,7 +104,7 @@ namespace MapControl
return;
}
}
else if (!tileSource.UriFormat.StartsWith("file:")) // always load local image files asynchronously
else if (!tileSource.UriFormat.StartsWith("file:")) // load local image files asynchronously, without caching
{
if (Cache == null || string.IsNullOrWhiteSpace(sourceName))
{
@ -183,7 +183,7 @@ namespace MapControl
if (uri != null)
{
if (uri.Scheme == "file")
if (uri.Scheme == "file") // create from FileStream as creating from URI leaves the file open
{
image = CreateImage(uri.AbsolutePath);
}
@ -256,7 +256,7 @@ namespace MapControl
{
try
{
using (var stream = new FileStream(path, FileMode.Open))
using (var stream = new FileStream(path, FileMode.Open, FileAccess.Read))
{
image = BitmapFrame.Create(stream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
}

View file

@ -48,7 +48,7 @@ namespace MapControl
public TileLayer()
{
MinZoomLevel = 1;
MinZoomLevel = 0;
MaxZoomLevel = 18;
MaxParallelDownloads = 8;
LoadLowerZoomLevels = true;

View file

@ -145,6 +145,11 @@ namespace MapControl
private Uri GetQuadKeyUri(int x, int y, int zoomLevel)
{
if (zoomLevel < 1)
{
return null;
}
var key = new StringBuilder { Length = zoomLevel };
for (var z = zoomLevel - 1; z >= 0; z--, x /= 2, y /= 2)

View file

@ -8,8 +8,8 @@ using System.Runtime.InteropServices;
[assembly: AssemblyCompany("Clemens Fischer")]
[assembly: AssemblyCopyright("Copyright © Clemens Fischer 2012-2013")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyVersion("1.8.0")]
[assembly: AssemblyFileVersion("1.8.0")]
[assembly: AssemblyVersion("1.9.0")]
[assembly: AssemblyFileVersion("1.9.0")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCulture("")]
[assembly: ComVisible(false)]