mirror of
https://github.com/ClemensFischer/XAML-Map-Control.git
synced 2026-01-31 04:44:14 +01:00
Attached property MapPanel.MapRect
This commit is contained in:
parent
881b39c4b4
commit
69b105c11f
|
|
@ -13,9 +13,12 @@ namespace MapControl
|
|||
public static readonly AttachedProperty<BoundingBox> BoundingBoxProperty =
|
||||
DependencyPropertyHelper.RegisterAttached<BoundingBox>("BoundingBox", typeof(MapPanel));
|
||||
|
||||
public static readonly AttachedProperty<MapRect> MapRectProperty =
|
||||
DependencyPropertyHelper.RegisterAttached<MapRect>("MapRect", typeof(MapPanel));
|
||||
|
||||
static MapPanel()
|
||||
{
|
||||
AffectsParentArrange<MapPanel>(LocationProperty, BoundingBoxProperty);
|
||||
AffectsParentArrange<MapPanel>(LocationProperty, BoundingBoxProperty, MapRectProperty);
|
||||
}
|
||||
|
||||
public static MapBase GetParentMap(FrameworkElement element)
|
||||
|
|
|
|||
|
|
@ -11,18 +11,13 @@ namespace MapControl
|
|||
#else
|
||||
[System.ComponentModel.TypeConverter(typeof(BoundingBoxConverter))]
|
||||
#endif
|
||||
public class BoundingBox(double latitude1, double longitude1, double latitude2, double longitude2, bool projectAxisAligned = false)
|
||||
public class BoundingBox(double latitude1, double longitude1, double latitude2, double longitude2)
|
||||
{
|
||||
public double South { get; } = Math.Min(Math.Max(Math.Min(latitude1, latitude2), -90d), 90d);
|
||||
public double North { get; } = Math.Min(Math.Max(Math.Max(latitude1, latitude2), -90d), 90d);
|
||||
public double West { get; } = Math.Min(longitude1, longitude2);
|
||||
public double East { get; } = Math.Max(longitude1, longitude2);
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether a MapProjection projects the BoundingBox to an axis-aligned or skewed rectangle.
|
||||
/// </summary>
|
||||
public bool ProjectAxisAligned { get; } = projectAxisAligned;
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format(CultureInfo.InvariantCulture, "{0},{1},{2},{3}", South, West, North, East);
|
||||
|
|
|
|||
|
|
@ -230,6 +230,14 @@ namespace MapControl
|
|||
return MapProjection.MapToLocation(ViewTransform.ViewToMap(point));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a MapRect in projected map coordinates that covers a rectangle in view coordinates.
|
||||
/// </summary>
|
||||
public MapRect ViewToMapRect(Rect rect)
|
||||
{
|
||||
return new MapRect(ViewTransform.ViewToMapBounds(rect), MapProjection.Center);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a BoundingBox in geographic coordinates that covers a rectangle in view coordinates.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -180,7 +180,7 @@ namespace MapControl
|
|||
updateTimer.Stop();
|
||||
|
||||
ImageSource image = null;
|
||||
BoundingBox boundingBox = null;
|
||||
MapRect mapRect = null;
|
||||
|
||||
if (ParentMap != null &&
|
||||
(SupportedCrsIds == null || SupportedCrsIds.Contains(ParentMap.MapProjection.CrsId)))
|
||||
|
|
@ -192,17 +192,13 @@ namespace MapControl
|
|||
{
|
||||
var x = (ParentMap.ActualWidth - width) / 2d;
|
||||
var y = (ParentMap.ActualHeight - height) / 2d;
|
||||
var mapRect = ParentMap.ViewTransform.ViewToMapBounds(new Rect(x, y, width, height));
|
||||
boundingBox = ParentMap.MapProjection.MapToBoundingBox(mapRect, true);
|
||||
|
||||
if (boundingBox != null)
|
||||
{
|
||||
image = await GetImageAsync(mapRect, loadingProgress);
|
||||
}
|
||||
mapRect = ParentMap.ViewToMapRect(new Rect(x, y, width, height));
|
||||
image = await GetImageAsync(mapRect.Rect, loadingProgress);
|
||||
}
|
||||
}
|
||||
|
||||
SwapImages(image, boundingBox);
|
||||
SwapImages(image, mapRect);
|
||||
updateInProgress = false;
|
||||
}
|
||||
else // update on next timer tick
|
||||
|
|
@ -215,12 +211,12 @@ namespace MapControl
|
|||
{
|
||||
foreach (var image in Children.OfType<Image>())
|
||||
{
|
||||
image.ClearValue(BoundingBoxProperty);
|
||||
image.ClearValue(MapRectProperty);
|
||||
image.ClearValue(Image.SourceProperty);
|
||||
}
|
||||
}
|
||||
|
||||
private void SwapImages(ImageSource image, BoundingBox boundingBox)
|
||||
private void SwapImages(ImageSource image, MapRect mapRect)
|
||||
{
|
||||
if (Children.Count >= 2)
|
||||
{
|
||||
|
|
@ -230,7 +226,7 @@ namespace MapControl
|
|||
Children.Insert(1, topImage);
|
||||
|
||||
topImage.Source = image;
|
||||
SetBoundingBox(topImage, boundingBox);
|
||||
SetMapRect(topImage, mapRect);
|
||||
|
||||
if (MapBase.ImageFadeDuration > TimeSpan.Zero)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -128,6 +128,22 @@ namespace MapControl
|
|||
element.SetValue(BoundingBoxProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the MapRect of an element.
|
||||
/// </summary>
|
||||
public static MapRect GetMapRect(FrameworkElement element)
|
||||
{
|
||||
return (MapRect)element.GetValue(MapRectProperty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the MapRect of an element.
|
||||
/// </summary>
|
||||
public static void SetMapRect(FrameworkElement element, MapRect value)
|
||||
{
|
||||
element.SetValue(MapRectProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the view position of an element with Location.
|
||||
/// </summary>
|
||||
|
|
@ -271,58 +287,66 @@ namespace MapControl
|
|||
{
|
||||
element.ClearValue(ViewPositionProperty);
|
||||
|
||||
var boundingBox = GetBoundingBox(element);
|
||||
var mapRect = GetMapRect(element);
|
||||
|
||||
if (boundingBox != null)
|
||||
if (mapRect != null)
|
||||
{
|
||||
ArrangeElement(element, boundingBox);
|
||||
mapRect.Update(parentMap.MapProjection);
|
||||
|
||||
ArrangeElement(element, mapRect.Rect, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
ArrangeElement(element, panelSize);
|
||||
var boundingBox = GetBoundingBox(element);
|
||||
|
||||
if (boundingBox != null)
|
||||
{
|
||||
(var rect, var transform) = parentMap.MapProjection.BoundingBoxToMap(boundingBox);
|
||||
|
||||
if (rect.HasValue)
|
||||
{
|
||||
ArrangeElement(element, rect.Value, transform);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ArrangeElement(element, panelSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ArrangeElement(FrameworkElement element, BoundingBox boundingBox)
|
||||
private void ArrangeElement(FrameworkElement element, Rect mapRect, Matrix? transform)
|
||||
{
|
||||
(var mapRect, var transform) = parentMap.MapProjection.BoundingBoxToMap(boundingBox);
|
||||
var viewRect = GetViewRect(mapRect);
|
||||
|
||||
if (mapRect.HasValue)
|
||||
element.Width = viewRect.Width;
|
||||
element.Height = viewRect.Height;
|
||||
element.Arrange(viewRect);
|
||||
|
||||
if (parentMap.ViewTransform.Rotation != 0d)
|
||||
{
|
||||
var viewRect = GetViewRect(mapRect.Value);
|
||||
var t = transform ?? new Matrix(1d, 0d, 0d, 1d, 0d, 0d);
|
||||
t.Rotate(parentMap.ViewTransform.Rotation);
|
||||
transform = t;
|
||||
}
|
||||
|
||||
element.Width = viewRect.Width;
|
||||
element.Height = viewRect.Height;
|
||||
element.Arrange(viewRect);
|
||||
|
||||
if (parentMap.ViewTransform.Rotation != 0d)
|
||||
if (element.RenderTransform is MatrixTransform matrixTransform &&
|
||||
!matrixTransform.Matrix.IsIdentity) // not default RenderTransform in WPF/UWP/WinUI
|
||||
{
|
||||
if (transform.HasValue)
|
||||
{
|
||||
if (!transform.HasValue)
|
||||
{
|
||||
transform = new Matrix(1d, 0d, 0d, 1d, 0d, 0d);
|
||||
}
|
||||
|
||||
transform.Value.Rotate(parentMap.ViewTransform.Rotation);
|
||||
matrixTransform.Matrix = transform.Value;
|
||||
}
|
||||
|
||||
if (element.RenderTransform is MatrixTransform matrixTransform &&
|
||||
!matrixTransform.Matrix.IsIdentity) // not default RenderTransform in WPF/UWP/WinUI
|
||||
else
|
||||
{
|
||||
if (transform.HasValue)
|
||||
{
|
||||
matrixTransform.Matrix = transform.Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
element.ClearValue(RenderTransformProperty);
|
||||
}
|
||||
}
|
||||
else if (transform.HasValue)
|
||||
{
|
||||
element.SetRenderTransform(new MatrixTransform { Matrix = transform.Value }, true);
|
||||
element.ClearValue(RenderTransformProperty);
|
||||
}
|
||||
}
|
||||
else if (transform.HasValue)
|
||||
{
|
||||
element.SetRenderTransform(new MatrixTransform { Matrix = transform.Value }, true);
|
||||
}
|
||||
}
|
||||
|
||||
private static void ArrangeElement(FrameworkElement element, Point position)
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ namespace MapControl
|
|||
/// </summary>
|
||||
public Location Center
|
||||
{
|
||||
get => center ??= new Location();
|
||||
get => center;
|
||||
set
|
||||
{
|
||||
updateCenter = true;
|
||||
|
|
@ -83,6 +83,7 @@ namespace MapControl
|
|||
protected void EnableCenterUpdates()
|
||||
{
|
||||
updateCenter = true;
|
||||
SetCenter(new Location());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -138,51 +139,39 @@ namespace MapControl
|
|||
public Location MapToLocation(Point point) => MapToLocation(point.X, point.Y);
|
||||
|
||||
/// <summary>
|
||||
/// Transforms a BoundingBox in geographic coordinates to a Rect in projected map coordinates
|
||||
/// with an optional transform Matrix when the BoundingBox is not projected axis-aligned.
|
||||
/// Returns (null, null) when the BoundingBox can not be transformed.
|
||||
/// Transforms a BoundingBox in geographic coordinates to a Rect in projected map coordinates with
|
||||
/// an optional transform Matrix. Returns (null, null) when the BoundingBox can not be transformed.
|
||||
/// </summary>
|
||||
public (Rect?, Matrix?) BoundingBoxToMap(BoundingBox boundingBox)
|
||||
{
|
||||
Rect? rect = null;
|
||||
Matrix? transform = null;
|
||||
var sw = LocationToMap(boundingBox.South, boundingBox.West);
|
||||
var se = LocationToMap(boundingBox.South, boundingBox.East);
|
||||
var nw = LocationToMap(boundingBox.North, boundingBox.West);
|
||||
var ne = LocationToMap(boundingBox.North, boundingBox.East);
|
||||
|
||||
if (sw.HasValue && ne.HasValue)
|
||||
if (sw.HasValue && se.HasValue && nw.HasValue && ne.HasValue)
|
||||
{
|
||||
if (boundingBox.ProjectAxisAligned)
|
||||
var south = new Point((sw.Value.X + se.Value.X) / 2d, (sw.Value.Y + se.Value.Y) / 2d); // south midpoint
|
||||
var north = new Point((nw.Value.X + ne.Value.X) / 2d, (nw.Value.Y + ne.Value.Y) / 2d); // north midpoint
|
||||
var west = new Point((nw.Value.X + sw.Value.X) / 2d, (nw.Value.Y + sw.Value.Y) / 2d); // west midpoint
|
||||
var east = new Point((ne.Value.X + se.Value.X) / 2d, (ne.Value.Y + se.Value.Y) / 2d); // east midpoint
|
||||
var center = new Point((west.X + east.X) / 2d, (west.Y + east.Y) / 2d); // midpoint of segment west-east
|
||||
var dx1 = east.X - west.X;
|
||||
var dy1 = east.Y - west.Y;
|
||||
var dx2 = north.X - south.X;
|
||||
var dy2 = north.Y - south.Y;
|
||||
var width = Math.Sqrt(dx1 * dx1 + dy1 * dy1); // distance west-east
|
||||
var height = Math.Sqrt(dx2 * dx2 + dy2 * dy2); // distance south-north
|
||||
|
||||
rect = new Rect(center.X - width / 2d, center.Y - height / 2d, width, height);
|
||||
|
||||
if (dy1 != 0d || dx2 != 0d)
|
||||
{
|
||||
rect = new Rect(sw.Value, ne.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
var se = LocationToMap(boundingBox.South, boundingBox.East);
|
||||
var nw = LocationToMap(boundingBox.North, boundingBox.West);
|
||||
|
||||
if (se.HasValue && nw.HasValue)
|
||||
{
|
||||
var south = new Point((sw.Value.X + se.Value.X) / 2d, (sw.Value.Y + se.Value.Y) / 2d); // south midpoint
|
||||
var north = new Point((nw.Value.X + ne.Value.X) / 2d, (nw.Value.Y + ne.Value.Y) / 2d); // north midpoint
|
||||
var west = new Point((nw.Value.X + sw.Value.X) / 2d, (nw.Value.Y + sw.Value.Y) / 2d); // west midpoint
|
||||
var east = new Point((ne.Value.X + se.Value.X) / 2d, (ne.Value.Y + se.Value.Y) / 2d); // east midpoint
|
||||
var center = new Point((west.X + east.X) / 2d, (west.Y + east.Y) / 2d); // midpoint of segment west-east
|
||||
var dx1 = east.X - west.X;
|
||||
var dy1 = east.Y - west.Y;
|
||||
var dx2 = north.X - south.X;
|
||||
var dy2 = north.Y - south.Y;
|
||||
var width = Math.Sqrt(dx1 * dx1 + dy1 * dy1); // distance west-east
|
||||
var height = Math.Sqrt(dx2 * dx2 + dy2 * dy2); // distance south-north
|
||||
|
||||
rect = new Rect(center.X - width / 2d, center.Y - height / 2d, width, height);
|
||||
|
||||
if (dy1 != 0d || dx2 != 0d)
|
||||
{
|
||||
// Skew matrix with skewX = Atan(-dx2 / dy2) and skewY = Atan(-dy1 / dx1).
|
||||
//
|
||||
transform = new Matrix(1d, -dy1 / dx1, -dx2 / dy2, 1d, 0d, 0d);
|
||||
}
|
||||
}
|
||||
// Skew matrix with skewX = Atan(-dx2 / dy2) and skewY = Atan(-dy1 / dx1).
|
||||
//
|
||||
transform = new Matrix(1d, -dy1 / dx1, -dx2 / dy2, 1d, 0d, 0d);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -193,14 +182,12 @@ namespace MapControl
|
|||
/// Transforms a Rect in projected map coordinates to a BoundingBox in geographic coordinates.
|
||||
/// Returns null when the Rect can not be transformed.
|
||||
/// </summary>
|
||||
public BoundingBox MapToBoundingBox(Rect rect, bool axisAligned = false)
|
||||
public BoundingBox MapToBoundingBox(Rect rect)
|
||||
{
|
||||
var sw = MapToLocation(rect.X, rect.Y);
|
||||
var ne = MapToLocation(rect.X + rect.Width, rect.Y + rect.Height);
|
||||
|
||||
return sw != null && ne != null
|
||||
? new BoundingBox(sw.Latitude, sw.Longitude, ne.Latitude, ne.Longitude, axisAligned)
|
||||
: null;
|
||||
return sw != null && ne != null ? new BoundingBox(sw.Latitude, sw.Longitude, ne.Latitude, ne.Longitude) : null;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
|
|
|
|||
27
MapControl/Shared/MapRect.cs
Normal file
27
MapControl/Shared/MapRect.cs
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
#if WPF
|
||||
using System.Windows;
|
||||
#elif AVALONIA
|
||||
using Avalonia;
|
||||
#endif
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
public class MapRect(Rect rect, Location origin)
|
||||
{
|
||||
public Rect Rect { get; private set; } = rect;
|
||||
public Location Origin { get; private set; } = origin;
|
||||
|
||||
public void Update(MapProjection projection)
|
||||
{
|
||||
Point? origin;
|
||||
|
||||
if (Origin != null && projection.Center != null &&
|
||||
!Origin.Equals(projection.Center) &&
|
||||
(origin = projection.LocationToMap(Origin)).HasValue)
|
||||
{
|
||||
Rect = new Rect(Rect.X + origin.Value.X, Rect.Y + origin.Value.Y, Rect.Width, Rect.Height);
|
||||
Origin = projection.Center;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -329,8 +329,16 @@ namespace MapControl
|
|||
|
||||
if (crs.StartsWith("AUTO2:") || crs.StartsWith("AUTO:"))
|
||||
{
|
||||
crs = string.Format(CultureInfo.InvariantCulture, "{0},1,{1:0.########},{2:0.########}",
|
||||
crs, projection.Center.Longitude, projection.Center.Latitude);
|
||||
var lon = 0d;
|
||||
var lat = 0d; ;
|
||||
|
||||
if (projection.Center != null)
|
||||
{
|
||||
lon = projection.Center.Longitude;
|
||||
lat = projection.Center.Latitude;
|
||||
}
|
||||
|
||||
crs = string.Format(CultureInfo.InvariantCulture, "{0},1,{1:0.########},{2:0.########}", crs, lon, lat);
|
||||
}
|
||||
|
||||
return crs;
|
||||
|
|
|
|||
|
|
@ -15,6 +15,10 @@ namespace MapControl
|
|||
DependencyPropertyHelper.RegisterAttached<BoundingBox>("BoundingBox", typeof(MapPanel), null,
|
||||
FrameworkPropertyMetadataOptions.AffectsParentArrange);
|
||||
|
||||
public static readonly DependencyProperty MapRectProperty =
|
||||
DependencyPropertyHelper.RegisterAttached<MapRect>("MapRect", typeof(MapPanel), null,
|
||||
FrameworkPropertyMetadataOptions.AffectsParentArrange);
|
||||
|
||||
public static MapBase GetParentMap(FrameworkElement element)
|
||||
{
|
||||
return (MapBase)element.GetValue(ParentMapProperty);
|
||||
|
|
|
|||
|
|
@ -21,6 +21,10 @@ namespace MapControl
|
|||
DependencyPropertyHelper.RegisterAttached<BoundingBox>("BoundingBox", typeof(MapPanel), null,
|
||||
(element, oldValue, newValue) => (element.Parent as MapPanel)?.InvalidateArrange());
|
||||
|
||||
public static readonly DependencyProperty MapRectProperty =
|
||||
DependencyPropertyHelper.RegisterAttached<MapRect>("MapRect", typeof(MapPanel), null,
|
||||
(element, oldValue, newValue) => (element.Parent as MapPanel)?.InvalidateArrange());
|
||||
|
||||
public static void InitMapElement(FrameworkElement element)
|
||||
{
|
||||
// Workaround for missing property value inheritance.
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ namespace MapControl.Projections
|
|||
public Wgs84OrthographicProjection()
|
||||
{
|
||||
EnableCenterUpdates();
|
||||
CenterChanged();
|
||||
}
|
||||
|
||||
protected override void CenterChanged()
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ namespace MapControl.Projections
|
|||
public Wgs84StereographicProjection()
|
||||
{
|
||||
EnableCenterUpdates();
|
||||
CenterChanged();
|
||||
}
|
||||
|
||||
protected override void CenterChanged()
|
||||
|
|
|
|||
Loading…
Reference in a new issue