mirror of
https://github.com/ClemensFischer/XAML-Map-Control.git
synced 2026-04-05 14:37:01 +00:00
Version 5.0: Reworked MapBase and MapPath
This commit is contained in:
parent
06fd31c17b
commit
49e15ce424
41 changed files with 466 additions and 1068 deletions
|
|
@ -69,12 +69,12 @@ namespace MapControl
|
|||
private DoubleAnimation zoomLevelAnimation;
|
||||
private DoubleAnimation headingAnimation;
|
||||
private Location transformCenter;
|
||||
private Point viewportCenter;
|
||||
private Point viewCenter;
|
||||
private double centerLongitude;
|
||||
private bool internalPropertyChange;
|
||||
|
||||
/// <summary>
|
||||
/// Raised when the current viewport has changed.
|
||||
/// Raised when the current map viewport has changed.
|
||||
/// </summary>
|
||||
public event EventHandler<ViewportChangedEventArgs> ViewportChanged;
|
||||
|
||||
|
|
@ -224,59 +224,56 @@ namespace MapControl
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the transformation from cartesian map coordinates to viewport coordinates.
|
||||
/// Gets the scaling factor from cartesian map coordinates to view coordinates,
|
||||
/// i.e. pixels per meter, as a read-only dependency property.
|
||||
/// </summary>
|
||||
public double ViewScale
|
||||
{
|
||||
get { return (double)GetValue(ViewScaleProperty); }
|
||||
private set { SetViewScale(value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the ViewTransform instance that is used to transform between cartesian map coordinates
|
||||
/// and view coordinates.
|
||||
/// </summary>
|
||||
public ViewTransform ViewTransform { get; } = new ViewTransform();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the transformation from cartesian map coordinates to viewport coordinates as MatrixTransform.
|
||||
/// Gets a MatrixTransform that can be used to transform from cartesian map coordinates
|
||||
/// to view coordinates.
|
||||
/// </summary>
|
||||
public MatrixTransform ViewportTransform { get; } = new MatrixTransform();
|
||||
public MatrixTransform MapToViewTransform { get; } = new MatrixTransform();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the scaling transformation from meters to viewport coordinates at the Center location.
|
||||
/// Gets the horizontal and vertical scaling factors from cartesian map coordinates to view
|
||||
/// coordinates at the specified location, i.e. pixels per meter.
|
||||
/// </summary>
|
||||
public ScaleTransform ScaleTransform { get; } = new ScaleTransform();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the transformation that rotates by the value of the Heading property.
|
||||
/// </summary>
|
||||
public RotateTransform RotateTransform { get; } = new RotateTransform();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the combination of ScaleTransform and RotateTransform
|
||||
/// </summary>
|
||||
public TransformGroup ScaleRotateTransform
|
||||
public Vector GetScale(Location location)
|
||||
{
|
||||
get
|
||||
{
|
||||
var transform = new TransformGroup();
|
||||
transform.Children.Add(ScaleTransform);
|
||||
transform.Children.Add(RotateTransform);
|
||||
return transform;
|
||||
}
|
||||
return ViewTransform.Scale * MapProjection.GetRelativeScale(location);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Transforms a Location in geographic coordinates to a Point in viewport coordinates.
|
||||
/// Transforms a Location in geographic coordinates to a Point in view coordinates.
|
||||
/// </summary>
|
||||
public Point LocationToViewportPoint(Location location)
|
||||
public Point LocationToView(Location location)
|
||||
{
|
||||
return ViewTransform.MapToView(MapProjection.LocationToMap(location));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Transforms a Point in viewport coordinates to a Location in geographic coordinates.
|
||||
/// Transforms a Point in view coordinates to a Location in geographic coordinates.
|
||||
/// </summary>
|
||||
public Location ViewportPointToLocation(Point point)
|
||||
public Location ViewToLocation(Point point)
|
||||
{
|
||||
return MapProjection.MapToLocation(ViewTransform.ViewToMap(point));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Transforms a Rect in viewport coordinates to a BoundingBox in geographic coordinates.
|
||||
/// Transforms a Rect in view coordinates to a BoundingBox in geographic coordinates.
|
||||
/// </summary>
|
||||
public BoundingBox ViewportRectToBoundingBox(Rect rect)
|
||||
public BoundingBox ViewRectToBoundingBox(Rect rect)
|
||||
{
|
||||
var p1 = ViewTransform.ViewToMap(new Point(rect.X, rect.Y));
|
||||
var p2 = ViewTransform.ViewToMap(new Point(rect.X, rect.Y + rect.Height));
|
||||
|
|
@ -292,13 +289,13 @@ namespace MapControl
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a temporary center point in viewport coordinates for scaling and rotation transformations.
|
||||
/// Sets a temporary center point in view coordinates for scaling and rotation transformations.
|
||||
/// This center point is automatically reset when the Center property is set by application code.
|
||||
/// </summary>
|
||||
public void SetTransformCenter(Point center)
|
||||
{
|
||||
transformCenter = ViewportPointToLocation(center);
|
||||
viewportCenter = center;
|
||||
transformCenter = ViewToLocation(center);
|
||||
viewCenter = center;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -307,11 +304,11 @@ namespace MapControl
|
|||
public void ResetTransformCenter()
|
||||
{
|
||||
transformCenter = null;
|
||||
viewportCenter = new Point(RenderSize.Width / 2d, RenderSize.Height / 2d);
|
||||
viewCenter = new Point(RenderSize.Width / 2d, RenderSize.Height / 2d);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Changes the Center property according to the specified translation in viewport coordinates.
|
||||
/// Changes the Center property according to the specified translation in view coordinates.
|
||||
/// </summary>
|
||||
public void TranslateMap(Vector translation)
|
||||
{
|
||||
|
|
@ -323,21 +320,21 @@ namespace MapControl
|
|||
|
||||
if (translation.X != 0d || translation.Y != 0d)
|
||||
{
|
||||
Center = ViewportPointToLocation(viewportCenter - translation);
|
||||
Center = ViewToLocation(viewCenter - translation);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Changes the Center, Heading and ZoomLevel properties according to the specified
|
||||
/// viewport coordinate translation, rotation and scale delta values. Rotation and scaling
|
||||
/// is performed relative to the specified center point in viewport coordinates.
|
||||
/// view coordinate translation, rotation and scale delta values. Rotation and scaling
|
||||
/// is performed relative to the specified center point in view coordinates.
|
||||
/// </summary>
|
||||
public void TransformMap(Point center, Vector translation, double rotation, double scale)
|
||||
{
|
||||
if (rotation != 0d || scale != 1d)
|
||||
{
|
||||
transformCenter = ViewportPointToLocation(center);
|
||||
viewportCenter = center + translation;
|
||||
transformCenter = ViewToLocation(center);
|
||||
viewCenter = center + translation;
|
||||
|
||||
if (rotation != 0d)
|
||||
{
|
||||
|
|
@ -363,7 +360,7 @@ namespace MapControl
|
|||
|
||||
/// <summary>
|
||||
/// Sets the value of the TargetZoomLevel property while retaining the specified center point
|
||||
/// in viewport coordinates.
|
||||
/// in view coordinates.
|
||||
/// </summary>
|
||||
public void ZoomMap(Point center, double zoomLevel)
|
||||
{
|
||||
|
|
@ -378,16 +375,15 @@ namespace MapControl
|
|||
|
||||
/// <summary>
|
||||
/// Sets the TargetZoomLevel and TargetCenter properties so that the specified bounding box
|
||||
/// fits into the current viewport. The TargetHeading property is set to zero.
|
||||
/// fits into the current view. The TargetHeading property is set to zero.
|
||||
/// </summary>
|
||||
public void ZoomToBounds(BoundingBox boundingBox)
|
||||
{
|
||||
var rect = MapProjection.BoundingBoxToRect(boundingBox);
|
||||
var center = new Point(rect.X + rect.Width / 2d, rect.Y + rect.Height / 2d);
|
||||
var scale = Math.Min(RenderSize.Width / rect.Width, RenderSize.Height / rect.Height)
|
||||
* MapProjection.Wgs84MetersPerDegree * 360d / 256d;
|
||||
var scale = Math.Min(RenderSize.Width / rect.Width, RenderSize.Height / rect.Height);
|
||||
|
||||
TargetZoomLevel = Math.Log(scale, 2d);
|
||||
TargetZoomLevel = ViewTransform.ScaleToZoomLevel(scale);
|
||||
TargetCenter = MapProjection.MapToLocation(center);
|
||||
TargetHeading = 0d;
|
||||
}
|
||||
|
|
@ -703,16 +699,19 @@ namespace MapControl
|
|||
private void UpdateTransform(bool resetTransformCenter = false, bool projectionChanged = false)
|
||||
{
|
||||
var projection = MapProjection;
|
||||
var viewportScale = 256d * Math.Pow(2d, ZoomLevel) / (360d * MapProjection.Wgs84MetersPerDegree);
|
||||
var center = transformCenter ?? Center;
|
||||
|
||||
projection.Center = ProjectionCenter ?? Center;
|
||||
|
||||
ViewTransform.SetTransform(projection.LocationToMap(center), viewportCenter, viewportScale, Heading);
|
||||
var center = transformCenter ?? Center;
|
||||
var mapCenter = projection.LocationToMap(center);
|
||||
|
||||
var viewScale = ViewTransform.ZoomLevelToScale(ZoomLevel);
|
||||
|
||||
ViewTransform.SetTransform(mapCenter, viewCenter, viewScale, Heading);
|
||||
|
||||
if (transformCenter != null)
|
||||
{
|
||||
center = ViewportPointToLocation(new Point(RenderSize.Width / 2d, RenderSize.Height / 2d));
|
||||
center = ViewToLocation(new Point(RenderSize.Width / 2d, RenderSize.Height / 2d));
|
||||
center.Longitude = Location.NormalizeLongitude(center.Longitude);
|
||||
|
||||
if (center.Latitude < -projection.MaxLatitude || center.Latitude > projection.MaxLatitude)
|
||||
|
|
@ -733,18 +732,14 @@ namespace MapControl
|
|||
ResetTransformCenter();
|
||||
|
||||
projection.Center = ProjectionCenter ?? center;
|
||||
mapCenter = projection.LocationToMap(center);
|
||||
|
||||
ViewTransform.SetTransform(projection.LocationToMap(center), viewportCenter, viewportScale, Heading);
|
||||
ViewTransform.SetTransform(mapCenter, viewCenter, viewScale, Heading);
|
||||
}
|
||||
}
|
||||
|
||||
ViewportTransform.Matrix = ViewTransform.MapToViewMatrix;
|
||||
|
||||
var scale = projection.GetRelativeScale(center);
|
||||
ScaleTransform.ScaleX = scale.X * ViewTransform.Scale;
|
||||
ScaleTransform.ScaleY = scale.Y * ViewTransform.Scale;
|
||||
|
||||
RotateTransform.Angle = ViewTransform.Rotation;
|
||||
ViewScale = ViewTransform.Scale;
|
||||
MapToViewTransform.Matrix = ViewTransform.MapToViewMatrix;
|
||||
|
||||
OnViewportChanged(new ViewportChangedEventArgs(projectionChanged, Center.Longitude - centerLongitude));
|
||||
|
||||
|
|
|
|||
|
|
@ -23,11 +23,14 @@ using System.Windows.Threading;
|
|||
namespace MapControl
|
||||
{
|
||||
/// <summary>
|
||||
/// Map image layer. Fills the entire viewport with a map image, e.g. provided by a Web Map Service.
|
||||
/// Map image layer. Fills the viewport with a single map image, e.g. provided by a Web Map Service.
|
||||
/// The image must be provided by the abstract GetImageAsync method.
|
||||
/// </summary>
|
||||
public abstract class MapImageLayer : MapPanel, IMapLayer
|
||||
{
|
||||
public static readonly DependencyProperty DescriptionProperty = DependencyProperty.Register(
|
||||
nameof(Description), typeof(string), typeof(MapImageLayer), new PropertyMetadata(null));
|
||||
|
||||
public static readonly DependencyProperty MinLatitudeProperty = DependencyProperty.Register(
|
||||
nameof(MinLatitude), typeof(double), typeof(MapImageLayer), new PropertyMetadata(double.NaN));
|
||||
|
||||
|
|
@ -53,9 +56,6 @@ namespace MapControl
|
|||
public static readonly DependencyProperty UpdateWhileViewportChangingProperty = DependencyProperty.Register(
|
||||
nameof(UpdateWhileViewportChanging), typeof(bool), typeof(MapImageLayer), new PropertyMetadata(false));
|
||||
|
||||
public static readonly DependencyProperty DescriptionProperty = DependencyProperty.Register(
|
||||
nameof(Description), typeof(string), typeof(MapImageLayer), new PropertyMetadata(null));
|
||||
|
||||
public static readonly DependencyProperty MapBackgroundProperty = DependencyProperty.Register(
|
||||
nameof(MapBackground), typeof(Brush), typeof(MapImageLayer), new PropertyMetadata(null));
|
||||
|
||||
|
|
@ -74,6 +74,16 @@ namespace MapControl
|
|||
updateTimer.Tick += async (s, e) => await UpdateImageAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Description of the MapImageLayer.
|
||||
/// Used to display copyright information on top of the map.
|
||||
/// </summary>
|
||||
public string Description
|
||||
{
|
||||
get { return (string)GetValue(DescriptionProperty); }
|
||||
set { SetValue(DescriptionProperty, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Optional minimum latitude value. Default is NaN.
|
||||
/// </summary>
|
||||
|
|
@ -120,9 +130,9 @@ namespace MapControl
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Relative size of the map image in relation to the current viewport size.
|
||||
/// Relative size of the map image in relation to the current view size.
|
||||
/// Setting a value greater than one will let MapImageLayer request images that
|
||||
/// are larger than the viewport, in order to support smooth panning.
|
||||
/// are larger than the view, in order to support smooth panning.
|
||||
/// </summary>
|
||||
public double RelativeImageSize
|
||||
{
|
||||
|
|
@ -148,16 +158,6 @@ namespace MapControl
|
|||
set { SetValue(UpdateWhileViewportChangingProperty, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Description of the MapImageLayer.
|
||||
/// Used to display copyright information on top of the map.
|
||||
/// </summary>
|
||||
public string Description
|
||||
{
|
||||
get { return (string)GetValue(DescriptionProperty); }
|
||||
set { SetValue(DescriptionProperty, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Optional foreground brush.
|
||||
/// Sets MapBase.Foreground if not null and the MapImageLayer is the base map layer.
|
||||
|
|
@ -258,7 +258,7 @@ namespace MapControl
|
|||
var y = (ParentMap.RenderSize.Height - height) / 2d;
|
||||
var rect = new Rect(x, y, width, height);
|
||||
|
||||
BoundingBox = ParentMap.ViewportRectToBoundingBox(rect);
|
||||
BoundingBox = ParentMap.ViewRectToBoundingBox(rect);
|
||||
|
||||
if (BoundingBox != null)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ namespace MapControl
|
|||
{
|
||||
SelectItems(item =>
|
||||
{
|
||||
var pos = MapPanel.GetViewportPosition(ContainerFromItem(item));
|
||||
var pos = MapPanel.GetViewPosition(ContainerFromItem(item));
|
||||
return pos.HasValue && predicate(pos.Value);
|
||||
});
|
||||
}
|
||||
|
|
@ -116,10 +116,10 @@ namespace MapControl
|
|||
}
|
||||
else if (shiftKey && SelectedItem != null)
|
||||
{
|
||||
// Extended with Shift -> select items in viewport rectangle
|
||||
// Extended with Shift -> select items in view rectangle
|
||||
|
||||
var p1 = MapPanel.GetViewportPosition(ContainerFromItem(SelectedItem));
|
||||
var p2 = MapPanel.GetViewportPosition(mapItem);
|
||||
var p1 = MapPanel.GetViewPosition(ContainerFromItem(SelectedItem));
|
||||
var p2 = MapPanel.GetViewPosition(mapItem);
|
||||
|
||||
if (p1.HasValue && p2.HasValue)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -58,9 +58,9 @@ namespace MapControl
|
|||
element.SetValue(BoundingBoxProperty, value);
|
||||
}
|
||||
|
||||
public static Point? GetViewportPosition(FrameworkElement element)
|
||||
public static Point? GetViewPosition(FrameworkElement element)
|
||||
{
|
||||
return (Point?)element.GetValue(ViewportPositionProperty);
|
||||
return (Point?)element.GetValue(ViewPositionProperty);
|
||||
}
|
||||
|
||||
public MapBase ParentMap
|
||||
|
|
@ -118,9 +118,9 @@ namespace MapControl
|
|||
|
||||
if (location != null)
|
||||
{
|
||||
var viewportPosition = ArrangeElement(element, location);
|
||||
var viewPosition = ArrangeElement(element, location);
|
||||
|
||||
SetViewportPosition(element, viewportPosition);
|
||||
SetViewPosition(element, viewPosition);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -135,7 +135,7 @@ namespace MapControl
|
|||
ArrangeElement(element, finalSize);
|
||||
}
|
||||
|
||||
SetViewportPosition(element, null);
|
||||
SetViewPosition(element, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -145,13 +145,13 @@ namespace MapControl
|
|||
|
||||
private Point ArrangeElement(FrameworkElement element, Location location)
|
||||
{
|
||||
var pos = parentMap.LocationToViewportPoint(location);
|
||||
var pos = parentMap.LocationToView(location);
|
||||
|
||||
if (parentMap.MapProjection.IsNormalCylindrical &&
|
||||
(pos.X < 0d || pos.X > parentMap.RenderSize.Width ||
|
||||
pos.Y < 0d || pos.Y > parentMap.RenderSize.Height))
|
||||
{
|
||||
pos = parentMap.LocationToViewportPoint(new Location(
|
||||
pos = parentMap.LocationToView(new Location(
|
||||
location.Latitude,
|
||||
Location.NearestLongitude(location.Longitude, parentMap.Center.Longitude)));
|
||||
}
|
||||
|
|
@ -210,7 +210,7 @@ namespace MapControl
|
|||
var location = projection.MapToLocation(center);
|
||||
location.Longitude = Location.NearestLongitude(location.Longitude, parentMap.Center.Longitude);
|
||||
|
||||
pos = parentMap.LocationToViewportPoint(location);
|
||||
pos = parentMap.LocationToView(location);
|
||||
}
|
||||
|
||||
rect.Width *= parentMap.ViewTransform.Scale;
|
||||
|
|
@ -229,14 +229,15 @@ namespace MapControl
|
|||
element.Arrange(rect);
|
||||
|
||||
var rotateTransform = element.RenderTransform as RotateTransform;
|
||||
var rotation = parentMap.ViewTransform.Rotation;
|
||||
|
||||
if (rotateTransform != null)
|
||||
{
|
||||
rotateTransform.Angle = parentMap.Heading;
|
||||
rotateTransform.Angle = rotation;
|
||||
}
|
||||
else if (parentMap.Heading != 0d)
|
||||
else if (rotation != 0d)
|
||||
{
|
||||
rotateTransform = new RotateTransform { Angle = parentMap.Heading };
|
||||
rotateTransform = new RotateTransform { Angle = rotation };
|
||||
element.RenderTransform = rotateTransform;
|
||||
element.RenderTransformOrigin = new Point(0.5, 0.5);
|
||||
}
|
||||
|
|
|
|||
142
MapControl/Shared/MapPath.cs
Normal file
142
MapControl/Shared/MapPath.cs
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
|
||||
// © 2020 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
#if WINDOWS_UWP
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Media;
|
||||
#else
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
#endif
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
/// <summary>
|
||||
/// A path element with a Data property that holds a Geometry in cartesian map coordinates
|
||||
/// or view coordinates. Cartesian coordinates can optionally be relative to an origin Location.
|
||||
/// </summary>
|
||||
public partial class MapPath : IMapElement
|
||||
{
|
||||
public static readonly DependencyProperty LocationProperty = DependencyProperty.Register(
|
||||
nameof(Location), typeof(Location), typeof(MapPath),
|
||||
new PropertyMetadata(null, (o, e) => ((MapPath)o).LocationOrViewportChanged()));
|
||||
|
||||
private MapBase parentMap;
|
||||
private MatrixTransform dataTransform;
|
||||
private double longitudeOffset;
|
||||
|
||||
public MapPath()
|
||||
{
|
||||
MapPanel.InitMapElement(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a Location that is used as
|
||||
/// - either the origin point of a geometry specified in cartesian map units (meters)
|
||||
/// - or as an optional value to constrain the view position of MapPaths with multiple
|
||||
/// Locations (like MapPolyline or MapPolygon) to the visible map viewport, as done
|
||||
/// for elements where the MapPanel.Location property is set.
|
||||
/// </summary>
|
||||
public Location Location
|
||||
{
|
||||
get { return (Location)GetValue(LocationProperty); }
|
||||
set { SetValue(LocationProperty, value); }
|
||||
}
|
||||
|
||||
public MapBase ParentMap
|
||||
{
|
||||
get { return parentMap; }
|
||||
set
|
||||
{
|
||||
if (parentMap != null)
|
||||
{
|
||||
parentMap.ViewportChanged -= OnViewportChanged;
|
||||
}
|
||||
|
||||
parentMap = value;
|
||||
|
||||
if (parentMap != null)
|
||||
{
|
||||
parentMap.ViewportChanged += OnViewportChanged;
|
||||
}
|
||||
|
||||
LocationOrViewportChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnViewportChanged(object sender, ViewportChangedEventArgs e)
|
||||
{
|
||||
LocationOrViewportChanged();
|
||||
}
|
||||
|
||||
private void LocationOrViewportChanged()
|
||||
{
|
||||
longitudeOffset = 0d;
|
||||
|
||||
if (parentMap != null && parentMap.MapProjection.IsNormalCylindrical && Location != null)
|
||||
{
|
||||
var viewPos = LocationToView(Location);
|
||||
|
||||
if (viewPos.X < 0d || viewPos.X > parentMap.RenderSize.Width ||
|
||||
viewPos.Y < 0d || viewPos.Y > parentMap.RenderSize.Height)
|
||||
{
|
||||
longitudeOffset = Location.NearestLongitude(Location.Longitude, parentMap.Center.Longitude) - Location.Longitude;
|
||||
}
|
||||
}
|
||||
|
||||
UpdateData();
|
||||
}
|
||||
|
||||
protected virtual void UpdateData()
|
||||
{
|
||||
if (parentMap != null && Data != null)
|
||||
{
|
||||
if (dataTransform == null)
|
||||
{
|
||||
Data.Transform = dataTransform = new MatrixTransform();
|
||||
}
|
||||
|
||||
if (Location != null)
|
||||
{
|
||||
var viewPos = LocationToView(Location);
|
||||
var scale = parentMap.GetScale(Location);
|
||||
var matrix = new Matrix(scale.X, 0, 0, scale.Y, 0, 0);
|
||||
matrix.Rotate(parentMap.Heading);
|
||||
matrix.Translate(viewPos.X, viewPos.Y);
|
||||
dataTransform.Matrix = matrix;
|
||||
}
|
||||
else
|
||||
{
|
||||
dataTransform.Matrix = parentMap.ViewTransform.MapToViewMatrix;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected Point LocationToMap(Location location)
|
||||
{
|
||||
if (longitudeOffset != 0d)
|
||||
{
|
||||
location = new Location(location.Latitude, location.Longitude + longitudeOffset);
|
||||
}
|
||||
|
||||
var point = parentMap.MapProjection.LocationToMap(location);
|
||||
|
||||
if (point.Y == double.PositiveInfinity)
|
||||
{
|
||||
point.Y = 1e9;
|
||||
}
|
||||
else if (point.X == double.NegativeInfinity)
|
||||
{
|
||||
point.Y = -1e9;
|
||||
}
|
||||
|
||||
return point;
|
||||
}
|
||||
|
||||
protected Point LocationToView(Location location)
|
||||
{
|
||||
return parentMap.ViewTransform.MapToView(LocationToMap(location));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -17,7 +17,7 @@ namespace MapControl
|
|||
/// <summary>
|
||||
/// A polygon defined by a collection of Locations.
|
||||
/// </summary>
|
||||
public class MapPolygon : MapShape
|
||||
public class MapPolygon : MapPath
|
||||
{
|
||||
public static readonly DependencyProperty LocationsProperty = DependencyProperty.Register(
|
||||
nameof(Locations), typeof(IEnumerable<Location>), typeof(MapPolygon),
|
||||
|
|
@ -35,6 +35,11 @@ namespace MapControl
|
|||
set { SetValue(LocationsProperty, value); }
|
||||
}
|
||||
|
||||
public MapPolygon()
|
||||
{
|
||||
Data = new PathGeometry();
|
||||
}
|
||||
|
||||
protected override void UpdateData()
|
||||
{
|
||||
var figures = ((PathGeometry)Data).Figures;
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ namespace MapControl
|
|||
/// <summary>
|
||||
/// A polyline defined by a collection of Locations.
|
||||
/// </summary>
|
||||
public class MapPolyline : MapShape
|
||||
public class MapPolyline : MapPath
|
||||
{
|
||||
public static readonly DependencyProperty LocationsProperty = DependencyProperty.Register(
|
||||
nameof(Locations), typeof(IEnumerable<Location>), typeof(MapPolyline),
|
||||
|
|
@ -35,6 +35,11 @@ namespace MapControl
|
|||
set { SetValue(LocationsProperty, value); }
|
||||
}
|
||||
|
||||
public MapPolyline()
|
||||
{
|
||||
Data = new PathGeometry();
|
||||
}
|
||||
|
||||
protected override void UpdateData()
|
||||
{
|
||||
var figures = ((PathGeometry)Data).Figures;
|
||||
|
|
|
|||
|
|
@ -59,9 +59,10 @@ namespace MapControl
|
|||
{
|
||||
var size = new Size();
|
||||
|
||||
if (ParentMap != null && ParentMap.ScaleTransform.ScaleX > 0d)
|
||||
if (ParentMap != null)
|
||||
{
|
||||
var length = MinWidth / ParentMap.ScaleTransform.ScaleX;
|
||||
var scale = ParentMap.GetScale(ParentMap.Center).X;
|
||||
var length = MinWidth / scale;
|
||||
var magnitude = Math.Pow(10d, Math.Floor(Math.Log10(length)));
|
||||
|
||||
if (length / magnitude < 2d)
|
||||
|
|
@ -77,7 +78,7 @@ namespace MapControl
|
|||
length = 10d * magnitude;
|
||||
}
|
||||
|
||||
size.Width = length * ParentMap.ScaleTransform.ScaleX + StrokeThickness + Padding.Left + Padding.Right;
|
||||
size.Width = length * scale + StrokeThickness + Padding.Left + Padding.Right;
|
||||
size.Height = 1.25 * FontSize + StrokeThickness + Padding.Top + Padding.Bottom;
|
||||
|
||||
var x1 = Padding.Left + StrokeThickness / 2d;
|
||||
|
|
|
|||
|
|
@ -1,125 +0,0 @@
|
|||
// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
|
||||
// © 2020 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
#if WINDOWS_UWP
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Media;
|
||||
#else
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
#endif
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class for MapPolyline and MapPolygon.
|
||||
/// </summary>
|
||||
public abstract partial class MapShape : IMapElement
|
||||
{
|
||||
public static readonly DependencyProperty LocationProperty = DependencyProperty.Register(
|
||||
nameof(Location), typeof(Location), typeof(MapShape),
|
||||
new PropertyMetadata(null, (o, e) => ((MapShape)o).LocationPropertyChanged()));
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets an optional Location to constrain the viewport position to the visible
|
||||
/// map viewport, as done for elements where the MapPanel.Location property is set.
|
||||
/// </summary>
|
||||
public Location Location
|
||||
{
|
||||
get { return (Location)GetValue(LocationProperty); }
|
||||
set { SetValue(LocationProperty, value); }
|
||||
}
|
||||
|
||||
private void LocationPropertyChanged()
|
||||
{
|
||||
if (parentMap != null)
|
||||
{
|
||||
UpdateData();
|
||||
}
|
||||
}
|
||||
|
||||
private MapBase parentMap;
|
||||
|
||||
public MapBase ParentMap
|
||||
{
|
||||
get { return parentMap; }
|
||||
set
|
||||
{
|
||||
if (parentMap != null)
|
||||
{
|
||||
parentMap.ViewportChanged -= OnViewportChanged;
|
||||
}
|
||||
|
||||
parentMap = value;
|
||||
|
||||
if (parentMap != null)
|
||||
{
|
||||
parentMap.ViewportChanged += OnViewportChanged;
|
||||
}
|
||||
|
||||
UpdateData();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnViewportChanged(object sender, ViewportChangedEventArgs e)
|
||||
{
|
||||
UpdateData();
|
||||
}
|
||||
|
||||
protected abstract void UpdateData();
|
||||
|
||||
protected MapShape()
|
||||
: this(new PathGeometry())
|
||||
{
|
||||
}
|
||||
|
||||
protected MapShape(Geometry data)
|
||||
{
|
||||
Data = data;
|
||||
|
||||
MapPanel.InitMapElement(this);
|
||||
}
|
||||
|
||||
protected Point LocationToMap(Location location)
|
||||
{
|
||||
var point = parentMap.MapProjection.LocationToMap(location);
|
||||
|
||||
if (point.Y == double.PositiveInfinity)
|
||||
{
|
||||
point.Y = 1e9;
|
||||
}
|
||||
else if (point.X == double.NegativeInfinity)
|
||||
{
|
||||
point.Y = -1e9;
|
||||
}
|
||||
|
||||
return point;
|
||||
}
|
||||
|
||||
protected Point LocationToViewportPoint(Location location)
|
||||
{
|
||||
return parentMap.ViewTransform.MapToView(LocationToMap(location));
|
||||
}
|
||||
|
||||
protected double GetLongitudeOffset()
|
||||
{
|
||||
var longitudeOffset = 0d;
|
||||
|
||||
if (parentMap.MapProjection.IsNormalCylindrical && Location != null)
|
||||
{
|
||||
var viewportPosition = LocationToViewportPoint(Location);
|
||||
|
||||
if (viewportPosition.X < 0d || viewportPosition.X > parentMap.RenderSize.Width ||
|
||||
viewportPosition.Y < 0d || viewportPosition.Y > parentMap.RenderSize.Height)
|
||||
{
|
||||
var nearestLongitude = Location.NearestLongitude(Location.Longitude, parentMap.Center.Longitude);
|
||||
|
||||
longitudeOffset = nearestLongitude - Location.Longitude;
|
||||
}
|
||||
}
|
||||
|
||||
return longitudeOffset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -17,7 +17,7 @@ using System.Windows.Media;
|
|||
namespace MapControl
|
||||
{
|
||||
/// <summary>
|
||||
/// Fills the map viewport with map tiles from a TileSource.
|
||||
/// Fills the viewport with map tiles from a TileSource.
|
||||
/// </summary>
|
||||
public class MapTileLayer : MapTileLayerBase
|
||||
{
|
||||
|
|
@ -26,11 +26,6 @@ namespace MapControl
|
|||
public static readonly Point TileMatrixTopLeft = new Point(
|
||||
-180d * MapProjection.Wgs84MetersPerDegree, 180d * MapProjection.Wgs84MetersPerDegree);
|
||||
|
||||
public static double TileMatrixScale(int zoomLevel)
|
||||
{
|
||||
return (TileSize << zoomLevel) / (360d * MapProjection.Wgs84MetersPerDegree);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A default MapTileLayer using OpenStreetMap data.
|
||||
/// </summary>
|
||||
|
|
@ -117,18 +112,22 @@ namespace MapControl
|
|||
//
|
||||
var tileMatrixOrigin = new Point(TileSize * TileMatrix.XMin, TileSize * TileMatrix.YMin);
|
||||
|
||||
var tileMatrixScale = ViewTransform.ZoomLevelToScale(TileMatrix.ZoomLevel);
|
||||
|
||||
((MatrixTransform)RenderTransform).Matrix = ParentMap.ViewTransform.GetTileLayerTransform(
|
||||
TileMatrixScale(TileMatrix.ZoomLevel), TileMatrixTopLeft, tileMatrixOrigin);
|
||||
tileMatrixScale, TileMatrixTopLeft, tileMatrixOrigin);
|
||||
}
|
||||
|
||||
private bool SetTileMatrix()
|
||||
{
|
||||
var tileMatrixZoomLevel = (int)Math.Floor(ParentMap.ZoomLevel + 0.001); // avoid rounding issues
|
||||
|
||||
// bounds in tile pixels from viewport size
|
||||
var tileMatrixScale = ViewTransform.ZoomLevelToScale(tileMatrixZoomLevel);
|
||||
|
||||
// bounds in tile pixels from view size
|
||||
//
|
||||
var tileBounds = ParentMap.ViewTransform.GetTileMatrixBounds(
|
||||
TileMatrixScale(tileMatrixZoomLevel), TileMatrixTopLeft, ParentMap.RenderSize);
|
||||
tileMatrixScale, TileMatrixTopLeft, ParentMap.RenderSize);
|
||||
|
||||
// tile column and row index bounds
|
||||
//
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
// © 2020 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using System;
|
||||
#if WINDOWS_UWP
|
||||
using Windows.Foundation;
|
||||
using Windows.UI.Xaml.Media;
|
||||
|
|
@ -13,22 +14,14 @@ using System.Windows.Media;
|
|||
namespace MapControl
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines the transformation between cartesian map coordinates and viewport coordinates.
|
||||
/// Defines the transformation between cartesian map coordinates in meters
|
||||
/// and view coordinates in pixels.
|
||||
/// </summary>
|
||||
public class ViewTransform
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the transform matrix from cartesian map coordinates to viewport coordinates.
|
||||
/// </summary>
|
||||
public Matrix MapToViewMatrix { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the transform matrix from viewport coordinates to cartesian map coordinates.
|
||||
/// </summary>
|
||||
public Matrix ViewToMapMatrix { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the scaling factor from cartesian map coordinates to viewport coordinates.
|
||||
/// Gets the scaling factor from cartesian map coordinates to view coordinates,
|
||||
/// i.e. pixels per meter.
|
||||
/// </summary>
|
||||
public double Scale { get; private set; }
|
||||
|
||||
|
|
@ -38,7 +31,17 @@ namespace MapControl
|
|||
public double Rotation { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Transforms a Point from cartesian map coordinates to viewport coordinates.
|
||||
/// Gets the transform matrix from cartesian map coordinates to view coordinates.
|
||||
/// </summary>
|
||||
public Matrix MapToViewMatrix { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the transform matrix from view coordinates to cartesian map coordinates.
|
||||
/// </summary>
|
||||
public Matrix ViewToMapMatrix { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Transforms a Point from cartesian map coordinates to view coordinates.
|
||||
/// </summary>
|
||||
public Point MapToView(Point point)
|
||||
{
|
||||
|
|
@ -46,14 +49,14 @@ namespace MapControl
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Transforms a Point from viewport coordinates to cartesian map coordinates.
|
||||
/// Transforms a Point from view coordinates to cartesian map coordinates.
|
||||
/// </summary>
|
||||
public Point ViewToMap(Point point)
|
||||
{
|
||||
return ViewToMapMatrix.Transform(point);
|
||||
}
|
||||
|
||||
public void SetTransform(Point mapCenter, Point viewportCenter, double scale, double rotation)
|
||||
public void SetTransform(Point mapCenter, Point viewCenter, double scale, double rotation)
|
||||
{
|
||||
Scale = scale;
|
||||
Rotation = rotation;
|
||||
|
|
@ -61,7 +64,7 @@ namespace MapControl
|
|||
var transform = new Matrix(Scale, 0d, 0d, -Scale, -Scale * mapCenter.X, Scale * mapCenter.Y);
|
||||
|
||||
transform.Rotate(Rotation);
|
||||
transform.Translate(viewportCenter.X, viewportCenter.Y);
|
||||
transform.Translate(viewCenter.X, viewCenter.Y);
|
||||
|
||||
MapToViewMatrix = transform;
|
||||
|
||||
|
|
@ -83,7 +86,7 @@ namespace MapControl
|
|||
tileMatrixTopLeft.X + tileMatrixOrigin.X / tileMatrixScale,
|
||||
tileMatrixTopLeft.Y - tileMatrixOrigin.Y / tileMatrixScale);
|
||||
|
||||
// tile matrix origin in viewport coordinates
|
||||
// tile matrix origin in view coordinates
|
||||
//
|
||||
var viewOrigin = MapToView(mapOrigin);
|
||||
|
||||
|
|
@ -92,14 +95,14 @@ namespace MapControl
|
|||
return transform;
|
||||
}
|
||||
|
||||
public Rect GetTileMatrixBounds(double tileMatrixScale, Point tileMatrixTopLeft, Size viewportSize)
|
||||
public Rect GetTileMatrixBounds(double tileMatrixScale, Point tileMatrixTopLeft, Size viewSize)
|
||||
{
|
||||
var transformScale = tileMatrixScale / Scale;
|
||||
var transform = new Matrix(transformScale, 0d, 0d, transformScale, 0d, 0d);
|
||||
|
||||
transform.Rotate(-Rotation);
|
||||
|
||||
// viewport origin in map coordinates
|
||||
// view origin in map coordinates
|
||||
//
|
||||
var origin = ViewToMap(new Point());
|
||||
|
||||
|
|
@ -109,10 +112,20 @@ namespace MapControl
|
|||
tileMatrixScale * (origin.X - tileMatrixTopLeft.X),
|
||||
tileMatrixScale * (tileMatrixTopLeft.Y - origin.Y));
|
||||
|
||||
// transform viewport bounds to tile pixel bounds
|
||||
// transform view bounds to tile pixel bounds
|
||||
//
|
||||
return new MatrixTransform { Matrix = transform }
|
||||
.TransformBounds(new Rect(0d, 0d, viewportSize.Width, viewportSize.Height));
|
||||
.TransformBounds(new Rect(0d, 0d, viewSize.Width, viewSize.Height));
|
||||
}
|
||||
|
||||
public static double ZoomLevelToScale(double zoomLevel)
|
||||
{
|
||||
return 256d * Math.Pow(2d, zoomLevel) / (360d * MapProjection.Wgs84MetersPerDegree);
|
||||
}
|
||||
|
||||
public static double ScaleToZoomLevel(double scale)
|
||||
{
|
||||
return Math.Log(scale * 360d * MapProjection.Wgs84MetersPerDegree / 256d, 2d);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ namespace MapControl
|
|||
|
||||
/// <summary>
|
||||
/// Indicates if the map projection has changed, i.e. if a MapTileLayer or MapImageLayer should
|
||||
/// be updated immediately, or MapShape Data in cartesian map coordinates should be recalculated.
|
||||
/// be updated immediately, or MapPath Data in cartesian map coordinates should be recalculated.
|
||||
/// </summary>
|
||||
public bool ProjectionChanged { get; }
|
||||
|
||||
|
|
|
|||
|
|
@ -47,11 +47,11 @@ namespace MapControl
|
|||
viewTransform.GetTileLayerTransform(TileMatrix.Scale, TileMatrix.TopLeft, tileMatrixOrigin);
|
||||
}
|
||||
|
||||
public bool SetBounds(ViewTransform viewTransform, Size viewportSize)
|
||||
public bool SetBounds(ViewTransform viewTransform, Size viewSize)
|
||||
{
|
||||
// bounds in tile pixels from viewport size
|
||||
// bounds in tile pixels from view size
|
||||
//
|
||||
var bounds = viewTransform.GetTileMatrixBounds(TileMatrix.Scale, TileMatrix.TopLeft, viewportSize);
|
||||
var bounds = viewTransform.GetTileMatrixBounds(TileMatrix.Scale, TileMatrix.TopLeft, viewSize);
|
||||
|
||||
// tile column and row index bounds
|
||||
//
|
||||
|
|
|
|||
|
|
@ -15,11 +15,11 @@ namespace MapControl
|
|||
/// </summary>
|
||||
public class WorldMercatorProjection : MapProjection
|
||||
{
|
||||
private static readonly double maxLatitude = YToLatitude(180d);
|
||||
|
||||
public static double ConvergenceTolerance = 1e-6;
|
||||
public static int MaxIterations = 10;
|
||||
|
||||
private static readonly double maxLatitude = YToLatitude(180d);
|
||||
|
||||
public WorldMercatorProjection()
|
||||
{
|
||||
CrsId = "EPSG:3395";
|
||||
|
|
|
|||
|
|
@ -39,6 +39,9 @@ namespace MapControl
|
|||
nameof(TargetHeading), typeof(double), typeof(MapBase),
|
||||
new PropertyMetadata(0d, (o, e) => ((MapBase)o).TargetHeadingPropertyChanged((double)e.NewValue)));
|
||||
|
||||
public static readonly DependencyProperty ViewScaleProperty = DependencyProperty.Register(
|
||||
nameof(ViewScale), typeof(double), typeof(MapBase), new PropertyMetadata(0d));
|
||||
|
||||
internal static readonly DependencyProperty CenterPointProperty = DependencyProperty.Register(
|
||||
"CenterPoint", typeof(Windows.Foundation.Point), typeof(MapBase),
|
||||
new PropertyMetadata(new Windows.Foundation.Point(), (o, e) => ((MapBase)o).CenterPointPropertyChanged((Windows.Foundation.Point)e.NewValue)));
|
||||
|
|
@ -62,6 +65,11 @@ namespace MapControl
|
|||
};
|
||||
}
|
||||
|
||||
private void SetViewScale(double scale)
|
||||
{
|
||||
SetValue(ViewScaleProperty, scale);
|
||||
}
|
||||
|
||||
private void CenterPointPropertyChanged(Windows.Foundation.Point center)
|
||||
{
|
||||
CenterPointPropertyChanged(new Location(center.Y, center.X));
|
||||
|
|
|
|||
|
|
@ -107,6 +107,9 @@
|
|||
<Compile Include="..\Shared\MapPanel.cs">
|
||||
<Link>MapPanel.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Shared\MapPath.cs">
|
||||
<Link>MapPath.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Shared\MapPolygon.cs">
|
||||
<Link>MapPolygon.cs</Link>
|
||||
</Compile>
|
||||
|
|
@ -119,9 +122,6 @@
|
|||
<Compile Include="..\Shared\MapScale.cs">
|
||||
<Link>MapScale.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Shared\MapShape.cs">
|
||||
<Link>MapShape.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Shared\MapTileLayer.cs">
|
||||
<Link>MapTileLayer.cs</Link>
|
||||
</Compile>
|
||||
|
|
@ -188,7 +188,7 @@
|
|||
<Compile Include="MapItemsControl.UWP.cs" />
|
||||
<Compile Include="MapOverlay.UWP.cs" />
|
||||
<Compile Include="MapPanel.UWP.cs" />
|
||||
<Compile Include="MapShape.UWP.cs" />
|
||||
<Compile Include="MapPath.UWP.cs" />
|
||||
<Compile Include="Matrix.UWP.cs" />
|
||||
<Compile Include="Point.UWP.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
|
|
|
|||
|
|
@ -4,11 +4,9 @@
|
|||
|
||||
using System;
|
||||
using Windows.Foundation;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Media;
|
||||
using Windows.UI.Xaml.Shapes;
|
||||
using Windows.UI.Xaml.Data;
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
|
|
@ -40,7 +38,7 @@ namespace MapControl
|
|||
Children.Add(path);
|
||||
}
|
||||
|
||||
var bounds = map.ViewportRectToBoundingBox(new Rect(0d, 0d, map.RenderSize.Width, map.RenderSize.Height));
|
||||
var bounds = map.ViewRectToBoundingBox(new Rect(0d, 0d, map.RenderSize.Width, map.RenderSize.Height));
|
||||
var lineDistance = GetLineDistance();
|
||||
|
||||
var labelStart = new Location(
|
||||
|
|
@ -66,14 +64,14 @@ namespace MapControl
|
|||
{
|
||||
var figure = new PathFigure
|
||||
{
|
||||
StartPoint = map.LocationToViewportPoint(new Location(lat, lineStart.Longitude)),
|
||||
StartPoint = map.LocationToView(new Location(lat, lineStart.Longitude)),
|
||||
IsClosed = false,
|
||||
IsFilled = false
|
||||
};
|
||||
|
||||
figure.Segments.Add(new LineSegment
|
||||
{
|
||||
Point = map.LocationToViewportPoint(new Location(lat, lineEnd.Longitude))
|
||||
Point = map.LocationToView(new Location(lat, lineEnd.Longitude))
|
||||
});
|
||||
|
||||
geometry.Figures.Add(figure);
|
||||
|
|
@ -83,14 +81,14 @@ namespace MapControl
|
|||
{
|
||||
var figure = new PathFigure
|
||||
{
|
||||
StartPoint = map.LocationToViewportPoint(new Location(lineStart.Latitude, lon)),
|
||||
StartPoint = map.LocationToView(new Location(lineStart.Latitude, lon)),
|
||||
IsClosed = false,
|
||||
IsFilled = false
|
||||
};
|
||||
|
||||
figure.Segments.Add(new LineSegment
|
||||
{
|
||||
Point = map.LocationToViewportPoint(new Location(lineEnd.Latitude, lon))
|
||||
Point = map.LocationToView(new Location(lineEnd.Latitude, lon))
|
||||
});
|
||||
|
||||
geometry.Figures.Add(figure);
|
||||
|
|
@ -111,22 +109,18 @@ namespace MapControl
|
|||
}
|
||||
else
|
||||
{
|
||||
var renderTransform = new TransformGroup();
|
||||
renderTransform.Children.Add(new TranslateTransform());
|
||||
renderTransform.Children.Add(map.RotateTransform);
|
||||
renderTransform.Children.Add(new TranslateTransform());
|
||||
|
||||
label = new TextBlock { RenderTransform = renderTransform };
|
||||
if (FontFamily != null)
|
||||
{
|
||||
label.SetBinding(TextBlock.FontFamilyProperty, GetBinding(FontFamilyProperty, nameof(FontFamily)));
|
||||
}
|
||||
label = new TextBlock { RenderTransform = new MatrixTransform() };
|
||||
label.SetBinding(TextBlock.FontSizeProperty, GetBinding(FontSizeProperty, nameof(FontSize)));
|
||||
label.SetBinding(TextBlock.FontStyleProperty, GetBinding(FontStyleProperty, nameof(FontStyle)));
|
||||
label.SetBinding(TextBlock.FontStretchProperty, GetBinding(FontStretchProperty, nameof(FontStretch)));
|
||||
label.SetBinding(TextBlock.FontWeightProperty, GetBinding(FontWeightProperty, nameof(FontWeight)));
|
||||
label.SetBinding(TextBlock.ForegroundProperty, GetBinding(ForegroundProperty, nameof(Foreground)));
|
||||
|
||||
if (FontFamily != null)
|
||||
{
|
||||
label.SetBinding(TextBlock.FontFamilyProperty, GetBinding(FontFamilyProperty, nameof(FontFamily)));
|
||||
}
|
||||
|
||||
Children.Add(label);
|
||||
}
|
||||
|
||||
|
|
@ -135,10 +129,6 @@ namespace MapControl
|
|||
label.Text = GetLabelText(lat, labelFormat, "NS") + "\n" + GetLabelText(Location.NormalizeLongitude(lon), labelFormat, "EW");
|
||||
label.Tag = new Location(lat, lon);
|
||||
label.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
|
||||
|
||||
var translateTransform = (TranslateTransform)((TransformGroup)label.RenderTransform).Children[0];
|
||||
translateTransform.X = StrokeThickness / 2d + 2d;
|
||||
translateTransform.Y = -label.DesiredSize.Height / 2d;
|
||||
}
|
||||
|
||||
while (Children.Count > childIndex)
|
||||
|
|
@ -153,10 +143,14 @@ namespace MapControl
|
|||
{
|
||||
var label = (TextBlock)Children[i];
|
||||
var location = (Location)label.Tag;
|
||||
var viewportTransform = (TranslateTransform)((TransformGroup)label.RenderTransform).Children[2];
|
||||
var viewportPosition = map.LocationToViewportPoint(location);
|
||||
viewportTransform.X = viewportPosition.X;
|
||||
viewportTransform.Y = viewportPosition.Y;
|
||||
var viewPosition = map.LocationToView(location);
|
||||
var matrix = new Matrix(1, 0, 0, 1, 0, 0);
|
||||
|
||||
matrix.Translate(StrokeThickness / 2d + 2d, -label.DesiredSize.Height / 2d);
|
||||
matrix.Rotate(map.ViewTransform.Rotation);
|
||||
matrix.Translate(viewPosition.X, viewPosition.Y);
|
||||
|
||||
((MatrixTransform)label.RenderTransform).Matrix = matrix;
|
||||
}
|
||||
}
|
||||
else if (path != null)
|
||||
|
|
|
|||
|
|
@ -20,8 +20,8 @@ namespace MapControl
|
|||
public static readonly DependencyProperty ParentMapProperty = DependencyProperty.RegisterAttached(
|
||||
"ParentMap", typeof(MapBase), typeof(MapPanel), new PropertyMetadata(null, ParentMapPropertyChanged));
|
||||
|
||||
private static readonly DependencyProperty ViewportPositionProperty = DependencyProperty.RegisterAttached(
|
||||
"ViewportPosition", typeof(Point?), typeof(MapPanel), new PropertyMetadata(null));
|
||||
private static readonly DependencyProperty ViewPositionProperty = DependencyProperty.RegisterAttached(
|
||||
"ViewPosition", typeof(Point?), typeof(MapPanel), new PropertyMetadata(null));
|
||||
|
||||
public static void InitMapElement(FrameworkElement element)
|
||||
{
|
||||
|
|
@ -61,9 +61,9 @@ namespace MapControl
|
|||
?? FindParentMap(parent));
|
||||
}
|
||||
|
||||
private static void SetViewportPosition(FrameworkElement element, Point? viewportPosition)
|
||||
private static void SetViewPosition(FrameworkElement element, Point? viewPosition)
|
||||
{
|
||||
element.SetValue(ViewportPositionProperty, viewportPosition);
|
||||
element.SetValue(ViewPositionProperty, viewPosition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ using Windows.UI.Xaml.Shapes;
|
|||
|
||||
namespace MapControl
|
||||
{
|
||||
public abstract partial class MapShape : Path
|
||||
public partial class MapPath : Path
|
||||
{
|
||||
protected void DataCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
|
|
@ -40,13 +40,7 @@ namespace MapControl
|
|||
{
|
||||
if (locations != null && locations.Count() >= 2)
|
||||
{
|
||||
var offset = GetLongitudeOffset();
|
||||
if (offset != 0d)
|
||||
{
|
||||
locations = locations.Select(loc => new Location(loc.Latitude, loc.Longitude + offset));
|
||||
}
|
||||
|
||||
var points = locations.Select(loc => LocationToViewportPoint(loc)).ToList();
|
||||
var points = locations.Select(loc => LocationToView(loc)).ToList();
|
||||
if (closed)
|
||||
{
|
||||
points.Add(points[0]);
|
||||
|
|
@ -50,6 +50,16 @@ namespace MapControl
|
|||
return new Vector(v1.X - v2.X, v1.Y - v2.Y);
|
||||
}
|
||||
|
||||
public static Vector operator *(double f, Vector v)
|
||||
{
|
||||
return new Vector(f * v.X, f * v.Y);
|
||||
}
|
||||
|
||||
public static Vector operator *(Vector v, double f)
|
||||
{
|
||||
return new Vector(f * v.X, f * v.Y);
|
||||
}
|
||||
|
||||
public static bool operator ==(Vector v1, Vector v2)
|
||||
{
|
||||
return v1.X == v2.X && v1.Y == v2.Y;
|
||||
|
|
|
|||
|
|
@ -43,6 +43,11 @@ namespace MapControl
|
|||
0d, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
|
||||
(o, e) => ((MapBase)o).TargetHeadingPropertyChanged((double)e.NewValue)));
|
||||
|
||||
private static readonly DependencyPropertyKey ViewScalePropertyKey = DependencyProperty.RegisterReadOnly(
|
||||
nameof(ViewScale), typeof(double), typeof(MapBase), new PropertyMetadata(0d));
|
||||
|
||||
public static readonly DependencyProperty ViewScaleProperty = ViewScalePropertyKey.DependencyProperty;
|
||||
|
||||
private static readonly DependencyProperty CenterPointProperty = DependencyProperty.Register(
|
||||
"CenterPoint", typeof(Point), typeof(MapBase),
|
||||
new PropertyMetadata(new Point(), (o, e) => ((MapBase)o).CenterPointPropertyChanged((Point)e.NewValue)));
|
||||
|
|
@ -61,6 +66,11 @@ namespace MapControl
|
|||
UpdateTransform();
|
||||
}
|
||||
|
||||
private void SetViewScale(double scale)
|
||||
{
|
||||
SetValue(ViewScalePropertyKey, scale);
|
||||
}
|
||||
|
||||
private void CenterPointPropertyChanged(Point center)
|
||||
{
|
||||
CenterPointPropertyChanged(new Location(center.Y, center.X));
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ namespace MapControl
|
|||
|
||||
private void DrawCylindricalGraticule(DrawingContext drawingContext, double lineDistance, string labelFormat)
|
||||
{
|
||||
var boundingBox = ParentMap.ViewportRectToBoundingBox(new Rect(ParentMap.RenderSize));
|
||||
var boundingBox = ParentMap.ViewRectToBoundingBox(new Rect(ParentMap.RenderSize));
|
||||
var latLabelStart = Math.Ceiling(boundingBox.South / lineDistance) * lineDistance;
|
||||
var lonLabelStart = Math.Ceiling(boundingBox.West / lineDistance) * lineDistance;
|
||||
var latLabels = new List<Label>((int)((boundingBox.North - latLabelStart) / lineDistance) + 1);
|
||||
|
|
@ -72,8 +72,8 @@ namespace MapControl
|
|||
CultureInfo.InvariantCulture, FlowDirection.LeftToRight, typeface, FontSize, Foreground, pixelsPerDip)));
|
||||
|
||||
drawingContext.DrawLine(pen,
|
||||
ParentMap.LocationToViewportPoint(new Location(lat, boundingBox.West)),
|
||||
ParentMap.LocationToViewportPoint(new Location(lat, boundingBox.East)));
|
||||
ParentMap.LocationToView(new Location(lat, boundingBox.West)),
|
||||
ParentMap.LocationToView(new Location(lat, boundingBox.East)));
|
||||
}
|
||||
|
||||
for (var lon = lonLabelStart; lon <= boundingBox.East; lon += lineDistance)
|
||||
|
|
@ -83,17 +83,17 @@ namespace MapControl
|
|||
CultureInfo.InvariantCulture, FlowDirection.LeftToRight, typeface, FontSize, Foreground, pixelsPerDip)));
|
||||
|
||||
drawingContext.DrawLine(pen,
|
||||
ParentMap.LocationToViewportPoint(new Location(boundingBox.South, lon)),
|
||||
ParentMap.LocationToViewportPoint(new Location(boundingBox.North, lon)));
|
||||
ParentMap.LocationToView(new Location(boundingBox.South, lon)),
|
||||
ParentMap.LocationToView(new Location(boundingBox.North, lon)));
|
||||
}
|
||||
|
||||
foreach (var latLabel in latLabels)
|
||||
{
|
||||
foreach (var lonLabel in lonLabels)
|
||||
{
|
||||
var position = ParentMap.LocationToViewportPoint(new Location(latLabel.Position, lonLabel.Position));
|
||||
var position = ParentMap.LocationToView(new Location(latLabel.Position, lonLabel.Position));
|
||||
|
||||
drawingContext.PushTransform(new RotateTransform(ParentMap.Heading, position.X, position.Y));
|
||||
drawingContext.PushTransform(new RotateTransform(ParentMap.ViewTransform.Rotation, position.X, position.Y));
|
||||
drawingContext.DrawText(latLabel.Text,
|
||||
new Point(position.X + StrokeThickness / 2d + 2d, position.Y - StrokeThickness / 2d - latLabel.Text.Height));
|
||||
drawingContext.DrawText(lonLabel.Text,
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ namespace MapControl
|
|||
/// for the Polygons property if collection changes of the property itself and its
|
||||
/// elements are both supposed to trigger a UI update.
|
||||
/// </summary>
|
||||
public class MapMultiPolygon : MapShape
|
||||
public class MapMultiPolygon : MapPath
|
||||
{
|
||||
public static readonly DependencyProperty PolygonsProperty = DependencyProperty.Register(
|
||||
nameof(Polygons), typeof(IEnumerable<IEnumerable<Location>>), typeof(MapMultiPolygon),
|
||||
|
|
@ -31,6 +31,11 @@ namespace MapControl
|
|||
set { SetValue(PolygonsProperty, value); }
|
||||
}
|
||||
|
||||
public MapMultiPolygon()
|
||||
{
|
||||
Data = new PathGeometry();
|
||||
}
|
||||
|
||||
protected override void UpdateData()
|
||||
{
|
||||
var figures = ((PathGeometry)Data).Figures;
|
||||
|
|
|
|||
|
|
@ -20,11 +20,11 @@ namespace MapControl
|
|||
"ParentMap", typeof(MapBase), typeof(MapPanel),
|
||||
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.Inherits, ParentMapPropertyChanged));
|
||||
|
||||
private static readonly DependencyPropertyKey ViewportPositionPropertyKey = DependencyProperty.RegisterAttachedReadOnly(
|
||||
"ViewportPosition", typeof(Point?), typeof(MapPanel), new PropertyMetadata());
|
||||
private static readonly DependencyPropertyKey ViewPositionPropertyKey = DependencyProperty.RegisterAttachedReadOnly(
|
||||
"ViewPosition", typeof(Point?), typeof(MapPanel), new PropertyMetadata());
|
||||
|
||||
public static readonly DependencyProperty ParentMapProperty = ParentMapPropertyKey.DependencyProperty;
|
||||
public static readonly DependencyProperty ViewportPositionProperty = ViewportPositionPropertyKey.DependencyProperty;
|
||||
public static readonly DependencyProperty ViewPositionProperty = ViewPositionPropertyKey.DependencyProperty;
|
||||
|
||||
public static MapBase GetParentMap(FrameworkElement element)
|
||||
{
|
||||
|
|
@ -39,9 +39,9 @@ namespace MapControl
|
|||
}
|
||||
}
|
||||
|
||||
private static void SetViewportPosition(FrameworkElement element, Point? viewportPosition)
|
||||
private static void SetViewPosition(FrameworkElement element, Point? viewPosition)
|
||||
{
|
||||
element.SetValue(ViewportPositionPropertyKey, viewportPosition);
|
||||
element.SetValue(ViewPositionPropertyKey, viewPosition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,9 +12,15 @@ using System.Windows.Shapes;
|
|||
|
||||
namespace MapControl
|
||||
{
|
||||
public abstract partial class MapShape : Shape, IWeakEventListener
|
||||
public partial class MapPath : Shape, IWeakEventListener
|
||||
{
|
||||
public Geometry Data { get; }
|
||||
public static readonly DependencyProperty DataProperty = Path.DataProperty.AddOwner(typeof(MapPath));
|
||||
|
||||
public Geometry Data
|
||||
{
|
||||
get { return (Geometry)GetValue(DataProperty); }
|
||||
set { SetValue(DataProperty, value); }
|
||||
}
|
||||
|
||||
protected override Geometry DefiningGeometry
|
||||
{
|
||||
|
|
@ -48,13 +54,7 @@ namespace MapControl
|
|||
{
|
||||
if (locations != null && locations.Count() >= 2)
|
||||
{
|
||||
var offset = GetLongitudeOffset();
|
||||
if (offset != 0d)
|
||||
{
|
||||
locations = locations.Select(loc => new Location(loc.Latitude, loc.Longitude + offset));
|
||||
}
|
||||
|
||||
var points = locations.Select(loc => LocationToViewportPoint(loc));
|
||||
var points = locations.Select(loc => LocationToView(loc));
|
||||
var figure = new PathFigure
|
||||
{
|
||||
StartPoint = points.First(),
|
||||
Loading…
Add table
Add a link
Reference in a new issue