Version 1.9.2: Improved transformation from Location to viewport position in MapPanel and MapGraticule.

This commit is contained in:
ClemensF 2013-11-18 19:49:17 +01:00
parent 68d9351044
commit b3474a3025
14 changed files with 115 additions and 81 deletions

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.9.1")]
[assembly: AssemblyFileVersion("1.9.1")]
[assembly: AssemblyVersion("1.9.2")]
[assembly: AssemblyFileVersion("1.9.2")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCulture("")]
[assembly: ComVisible(false)]

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.9.1")]
[assembly: AssemblyFileVersion("1.9.1")]
[assembly: AssemblyVersion("1.9.2")]
[assembly: AssemblyFileVersion("1.9.2")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCulture("")]
[assembly: ComVisible(false)]

View file

@ -22,7 +22,7 @@ namespace MapControl
#if !SILVERLIGHT && !NETFX_CORE
[NonSerialized]
#endif
internal double TransformedLatitude;
internal double TransformedLatitude = double.NaN;
private double latitude;
private double longitude;
@ -31,10 +31,16 @@ namespace MapControl
{
}
public Location(double lat, double lon)
public Location(double latitude, double longitude)
{
Latitude = lat;
Longitude = lon;
this.latitude = Math.Min(Math.Max(latitude, -90d), 90d);
this.longitude = longitude;
}
internal Location(double transformedLatitude, double latitude, double longitude)
: this(latitude, longitude)
{
TransformedLatitude = transformedLatitude;
}
public double Latitude

View file

@ -3,6 +3,7 @@
// Licensed under the Microsoft Public License (Ms-PL)
using System;
using System.Collections.Generic;
using System.Linq;
#if NETFX_CORE
using Windows.Foundation;
@ -160,59 +161,69 @@ namespace MapControl
spacing = LineSpacings.FirstOrDefault(s => s >= minSpacing);
}
var labelsStart = new Location(
var labelStart = new Location(
Math.Ceiling(start.Latitude / spacing) * spacing,
Math.Ceiling(start.Longitude / spacing) * spacing);
var labelsEnd = new Location(
var labelEnd = new Location(
Math.Floor(end.Latitude / spacing) * spacing,
Math.Floor(end.Longitude / spacing) * spacing);
var linesStart = new Location(
Math.Min(Math.Max(labelsStart.Latitude - spacing, -ParentMap.MapTransform.MaxLatitude), ParentMap.MapTransform.MaxLatitude),
labelsStart.Longitude - spacing);
var lineStart = new Location(
Math.Min(Math.Max(labelStart.Latitude - spacing, -ParentMap.MapTransform.MaxLatitude), ParentMap.MapTransform.MaxLatitude),
labelStart.Longitude - spacing);
var linesEnd = new Location(
Math.Min(Math.Max(labelsEnd.Latitude + spacing, -ParentMap.MapTransform.MaxLatitude), ParentMap.MapTransform.MaxLatitude),
labelsEnd.Longitude + spacing);
var lineEnd = new Location(
Math.Min(Math.Max(labelEnd.Latitude + spacing, -ParentMap.MapTransform.MaxLatitude), ParentMap.MapTransform.MaxLatitude),
labelEnd.Longitude + spacing);
if (!linesStart.Equals(graticuleStart) || !linesEnd.Equals(graticuleEnd))
if (!lineStart.Equals(graticuleStart) || !lineEnd.Equals(graticuleEnd))
{
graticuleStart = linesStart;
graticuleEnd = linesEnd;
ParentMap.MapTransform.Transform(lineStart); // get lineStart.TransformedLatitude
ParentMap.MapTransform.Transform(lineEnd); // get lineEnd.TransformedLatitude
graticuleStart = lineStart;
graticuleEnd = lineEnd;
geometry.Figures.Clear();
geometry.Transform = ParentMap.ViewportTransform;
for (var lat = labelsStart.Latitude; lat <= end.Latitude; lat += spacing)
var latLocations = new List<Location>((int)((end.Latitude - labelStart.Latitude) / spacing) + 1);
for (var lat = labelStart.Latitude; lat <= end.Latitude; lat += spacing)
{
var location = new Location(lat, lineStart.Longitude);
latLocations.Add(location);
var figure = new PathFigure
{
StartPoint = ParentMap.MapTransform.Transform(new Location(lat, linesStart.Longitude)),
StartPoint = ParentMap.MapTransform.Transform(location),
IsClosed = false,
IsFilled = false
};
location.Longitude = lineEnd.Longitude;
figure.Segments.Add(new LineSegment
{
Point = ParentMap.MapTransform.Transform(new Location(lat, linesEnd.Longitude)),
Point = ParentMap.MapTransform.Transform(location),
});
geometry.Figures.Add(figure);
}
for (var lon = labelsStart.Longitude; lon <= end.Longitude; lon += spacing)
for (var lon = labelStart.Longitude; lon <= end.Longitude; lon += spacing)
{
var figure = new PathFigure
{
StartPoint = ParentMap.MapTransform.Transform(new Location(linesStart.Latitude, lon)),
StartPoint = ParentMap.MapTransform.Transform(new Location(lineStart.TransformedLatitude, lineStart.Latitude, lon)),
IsClosed = false,
IsFilled = false
};
figure.Segments.Add(new LineSegment
{
Point = ParentMap.MapTransform.Transform(new Location(linesEnd.Latitude, lon)),
Point = ParentMap.MapTransform.Transform(new Location(lineEnd.TransformedLatitude, lineEnd.Latitude, lon)),
});
geometry.Figures.Add(figure);
@ -222,10 +233,12 @@ namespace MapControl
var format = spacing < 1d ? "{0} {1}°{2:00}'" : "{0} {1}°";
var measureSize = new Size(double.PositiveInfinity, double.PositiveInfinity);
for (var lat = labelsStart.Latitude; lat <= end.Latitude; lat += spacing)
foreach (var location in latLocations)
{
for (var lon = labelsStart.Longitude; lon <= end.Longitude; lon += spacing)
for (var lon = labelStart.Longitude; lon <= end.Longitude; lon += spacing)
{
location.Longitude = lon;
TextBlock label;
if (childIndex < Children.Count)
@ -261,8 +274,8 @@ namespace MapControl
label.FontWeight = FontWeight;
label.Text = string.Format("{0}\n{1}",
CoordinateString(lat, format, "NS"),
CoordinateString(Location.NormalizeLongitude(lon), format, "EW"));
CoordinateString(location.Latitude, format, "NS"),
CoordinateString(Location.NormalizeLongitude(location.Longitude), format, "EW"));
label.Measure(measureSize);
@ -279,7 +292,7 @@ namespace MapControl
translateTransform.X = StrokeThickness / 2d + 2d;
translateTransform.Y = -label.DesiredSize.Height / 2d;
var viewportPosition = ParentMap.LocationToViewportPoint(new Location(lat, lon));
var viewportPosition = ParentMap.LocationToViewportPoint(location);
translateTransform = (TranslateTransform)transformGroup.Children[2];
translateTransform.X = viewportPosition.X;
translateTransform.Y = viewportPosition.Y;

View file

@ -12,14 +12,28 @@ namespace MapControl
{
public partial class MapGraticule : MapOverlay
{
private class Label
private class LonLabel
{
public readonly double Position;
public readonly double Longitude;
public readonly string Text;
public Label(double position, string text)
public LonLabel(double longitude, string text)
{
Position = position;
Longitude = longitude;
Text = text;
}
}
private class LatLabel
{
public readonly double TransformedLatitude;
public readonly double Latitude;
public readonly string Text;
public LatLabel(double transformedLatitude, double latitude, string text)
{
TransformedLatitude = transformedLatitude;
Latitude = latitude;
Text = text;
}
}
@ -45,8 +59,10 @@ namespace MapControl
if (ParentMap != null)
{
var bounds = ParentMap.ViewportTransform.Inverse.TransformBounds(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 startPoint = new Point(bounds.X, bounds.Y);
var endPoint = new Point(bounds.X + bounds.Width, bounds.Y + bounds.Height);
var startLocation = ParentMap.MapTransform.Transform(startPoint);
var endLocation = ParentMap.MapTransform.Transform(endPoint);
var minSpacing = MinLineSpacing * 360d / (Math.Pow(2d, ParentMap.ZoomLevel) * TileSource.TileSize);
var spacing = LineSpacings[LineSpacings.Length - 1];
@ -57,28 +73,31 @@ namespace MapControl
var labelFormat = spacing < 1d ? "{0} {1}°{2:00}'" : "{0} {1}°";
var labelStart = new Location(
Math.Ceiling(start.Latitude / spacing) * spacing,
Math.Ceiling(start.Longitude / spacing) * spacing);
Math.Ceiling(startLocation.Latitude / spacing) * spacing,
Math.Ceiling(startLocation.Longitude / spacing) * spacing);
var latLabels = new List<Label>(16);
var lonLabels = new List<Label>(16);
var latLabels = new List<LatLabel>((int)((endLocation.Latitude - labelStart.Latitude) / spacing) + 1);
var lonLabels = new List<LonLabel>((int)((endLocation.Longitude - labelStart.Longitude) / spacing) + 1);
for (var lat = labelStart.Latitude; lat <= end.Latitude; lat += spacing)
for (var lat = labelStart.Latitude; lat <= endLocation.Latitude; lat += spacing)
{
latLabels.Add(new Label(lat, CoordinateString(lat, labelFormat, "NS")));
var location = new Location(lat, startLocation.Longitude);
var p1 = ParentMap.LocationToViewportPoint(location);
location.Longitude = endLocation.Longitude;
var p2 = ParentMap.LocationToViewportPoint(location);
drawingContext.DrawLine(Pen,
ParentMap.LocationToViewportPoint(new Location(lat, start.Longitude)),
ParentMap.LocationToViewportPoint(new Location(lat, end.Longitude)));
latLabels.Add(new LatLabel(location.TransformedLatitude, lat, CoordinateString(lat, labelFormat, "NS")));
drawingContext.DrawLine(Pen, p1, p2);
}
for (var lon = labelStart.Longitude; lon <= end.Longitude; lon += spacing)
for (var lon = labelStart.Longitude; lon <= endLocation.Longitude; lon += spacing)
{
lonLabels.Add(new Label(lon, CoordinateString(Location.NormalizeLongitude(lon), labelFormat, "EW")));
lonLabels.Add(new LonLabel(lon, CoordinateString(Location.NormalizeLongitude(lon), labelFormat, "EW")));
drawingContext.DrawLine(Pen,
ParentMap.LocationToViewportPoint(new Location(start.Latitude, lon)),
ParentMap.LocationToViewportPoint(new Location(end.Latitude, lon)));
ParentMap.LocationToViewportPoint(new Location(startPoint.Y, startLocation.Latitude, lon)),
ParentMap.LocationToViewportPoint(new Location(endPoint.Y, endLocation.Latitude, lon)));
}
if (Foreground != null && Foreground != Brushes.Transparent && latLabels.Count > 0 && lonLabels.Count > 0)
@ -107,7 +126,7 @@ namespace MapControl
glyphRuns.Add(lonLabel.Text, lonGlyphRun);
}
var position = ParentMap.LocationToViewportPoint(new Location(latLabel.Position, lonLabel.Position));
var position = ParentMap.LocationToViewportPoint(new Location(latLabel.TransformedLatitude, latLabel.Latitude, lonLabel.Longitude));
drawingContext.PushTransform(new MatrixTransform(
transform.M11, transform.M12, transform.M21, transform.M22, position.X, position.Y));

View file

@ -23,8 +23,8 @@ namespace MapControl
/// <summary>
/// Positions child elements on a Map, at a position specified by the attached property Location.
/// The Location is transformed into a viewport position by the MapBase.LocationToViewportPoint
/// method and applied to a child element's RenderTransform as an appropriate TranslateTransform.
/// The Location is transformed to a viewport position by ParentMap.MapTransform and ParentMap.ViewportTransform
/// and applied to a child element's RenderTransform as an appropriate TranslateTransform.
/// </summary>
public partial class MapPanel : Panel, IMapElement
{
@ -151,24 +151,22 @@ namespace MapControl
if (parentMap != null && location != null)
{
var longitude = Location.NormalizeLongitude(location.Longitude);
var centerDistance = longitude - parentMap.Center.Longitude;
// transform location before checking longitude to keep location.TransformedLatitude
var mapPosition = parentMap.MapTransform.Transform(location);
mapPosition.X = Location.NormalizeLongitude(mapPosition.X);
if (centerDistance > 180d)
var centerOffset = mapPosition.X - parentMap.Center.Longitude; // keep viewport position near map center
if (centerOffset > 180d)
{
longitude -= 360d;
mapPosition.X -= 360d;
}
else if (centerDistance < -180d)
else if (centerOffset < -180d)
{
longitude += 360d;
mapPosition.X += 360d;
}
if (location.Longitude != longitude) // keep viewport position near map center
{
location = new Location(location.Latitude, longitude);
}
viewportPosition = parentMap.LocationToViewportPoint(location);
viewportPosition = parentMap.ViewportTransform.Transform(mapPosition);
element.SetValue(ViewportPositionProperty, viewportPosition);
}
else

View file

@ -63,9 +63,7 @@ namespace MapControl
public override Location Transform(Point point)
{
var location = new Location(Math.Atan(Math.Sinh(point.Y * Math.PI / 180d)) / Math.PI * 180d, point.X);
location.TransformedLatitude = point.Y;
return location;
return new Location(point.Y, Math.Atan(Math.Sinh(point.Y * Math.PI / 180d)) / Math.PI * 180d, point.X);
}
}
}

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.9.1")]
[assembly: AssemblyFileVersion("1.9.1")]
[assembly: AssemblyVersion("1.9.2")]
[assembly: AssemblyFileVersion("1.9.2")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCulture("")]
[assembly: ComVisible(false)]

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.9.1")]
[assembly: AssemblyFileVersion("1.9.1")]
[assembly: AssemblyVersion("1.9.2")]
[assembly: AssemblyFileVersion("1.9.2")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCulture("")]
[assembly: ComVisible(false)]

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.9.1")]
[assembly: AssemblyFileVersion("1.9.1")]
[assembly: AssemblyVersion("1.9.2")]
[assembly: AssemblyFileVersion("1.9.2")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCulture("")]
[assembly: ComVisible(false)]

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.9.1")]
[assembly: AssemblyFileVersion("1.9.1")]
[assembly: AssemblyVersion("1.9.2")]
[assembly: AssemblyFileVersion("1.9.2")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCulture("")]
[assembly: ComVisible(false)]

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.9.1")]
[assembly: AssemblyFileVersion("1.9.1")]
[assembly: AssemblyVersion("1.9.2")]
[assembly: AssemblyFileVersion("1.9.2")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCulture("")]
[assembly: ComVisible(false)]

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.9.1")]
[assembly: AssemblyFileVersion("1.9.1")]
[assembly: AssemblyVersion("1.9.2")]
[assembly: AssemblyFileVersion("1.9.2")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCulture("")]
[assembly: ComVisible(false)]

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.9.1")]
[assembly: AssemblyFileVersion("1.9.1")]
[assembly: AssemblyVersion("1.9.2")]
[assembly: AssemblyFileVersion("1.9.2")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCulture("")]
[assembly: ComVisible(false)]