mirror of
https://github.com/ClemensFischer/XAML-Map-Control.git
synced 2025-12-06 07:12:04 +01:00
MapPanel longitude coercion
This commit is contained in:
parent
94c3faf805
commit
4b0a0d29fd
|
|
@ -12,7 +12,6 @@
|
|||
|
||||
<ItemGroup>
|
||||
<Compile Include="..\Shared\*.cs" />
|
||||
<Compile Remove="..\Shared\ViewTransform.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
|||
|
|
@ -2,47 +2,10 @@
|
|||
// Copyright © 2024 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using System;
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines the transformation between projected map coordinates in meters
|
||||
/// and view coordinates in pixels.
|
||||
/// </summary>
|
||||
public class ViewTransform
|
||||
public partial class ViewTransform
|
||||
{
|
||||
public static double ZoomLevelToScale(double zoomLevel)
|
||||
{
|
||||
return 256d * Math.Pow(2d, zoomLevel) / (360d * MapProjection.Wgs84MeterPerDegree);
|
||||
}
|
||||
|
||||
public static double ScaleToZoomLevel(double scale)
|
||||
{
|
||||
return Math.Log(scale * 360d * MapProjection.Wgs84MeterPerDegree / 256d, 2d);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the scaling factor from projected map coordinates to view coordinates,
|
||||
/// as pixels per meter.
|
||||
/// </summary>
|
||||
public double Scale { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the rotation angle of the transform matrix.
|
||||
/// </summary>
|
||||
public double Rotation { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the transform matrix from projected map coordinates to view coordinates.
|
||||
/// </summary>
|
||||
public Matrix MapToViewMatrix { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the transform matrix from view coordinates to projected map coordinates.
|
||||
/// </summary>
|
||||
public Matrix ViewToMapMatrix { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a ViewTransform from a map center point in projected coordinates,
|
||||
/// a view conter point, a scaling factor from projected coordinates to view coordinates
|
||||
|
|
@ -62,31 +25,6 @@ namespace MapControl
|
|||
ViewToMapMatrix = MapToViewMatrix.Invert();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Transforms a Point from projected map coordinates to view coordinates.
|
||||
/// </summary>
|
||||
public Point MapToView(Point point)
|
||||
{
|
||||
return MapToViewMatrix.Transform(point);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Transforms a Point from view coordinates to projected map coordinates.
|
||||
/// </summary>
|
||||
public Point ViewToMap(Point point)
|
||||
{
|
||||
return ViewToMapMatrix.Transform(point);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Transform relative to absolute map scale. Returns horizontal and vertical
|
||||
/// scaling factors from meters to view coordinates.
|
||||
/// </summary>
|
||||
public Point GetMapScale(Point relativeScale)
|
||||
{
|
||||
return new Point(Scale * relativeScale.X, Scale * relativeScale.Y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a transform Matrix from meters to view coordinates for a relative map scale.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ namespace MapControl
|
|||
#endif
|
||||
public class BoundingBox
|
||||
{
|
||||
public BoundingBox()
|
||||
protected BoundingBox()
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -34,15 +34,27 @@ namespace MapControl
|
|||
{
|
||||
}
|
||||
|
||||
public double South { get; }
|
||||
public double North { get; }
|
||||
public double West { get; }
|
||||
public double East { get; }
|
||||
public double South { get; private set; }
|
||||
public double North { get; private set; }
|
||||
public double West { get; private set; }
|
||||
public double East { get; private set; }
|
||||
|
||||
public virtual double Width => East - West;
|
||||
public virtual double Height => North - South;
|
||||
|
||||
public virtual Location Center => new Location((South + North) / 2d, (West + East) / 2d);
|
||||
public virtual Location Center
|
||||
{
|
||||
get => new Location((South + North) / 2d, (West + East) / 2d);
|
||||
set
|
||||
{
|
||||
var latOffset = value.Latitude - (South + North) / 2d;
|
||||
var lonOffset = value.Longitude - (West + East) / 2d;
|
||||
South += latOffset;
|
||||
North += latOffset;
|
||||
West += lonOffset;
|
||||
East += lonOffset;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a BoundingBox instance from a string containing a comma-separated sequence of four floating point numbers.
|
||||
|
|
|
|||
|
|
@ -8,19 +8,15 @@ namespace MapControl
|
|||
{
|
||||
public class CenteredBoundingBox : BoundingBox
|
||||
{
|
||||
private readonly Location center;
|
||||
private readonly double width;
|
||||
private readonly double height;
|
||||
|
||||
public CenteredBoundingBox(Location c, double w, double h)
|
||||
public CenteredBoundingBox(Location center, double width, double height)
|
||||
{
|
||||
center = c;
|
||||
width = Math.Max(w, 0d);
|
||||
height = Math.Max(h, 0d);
|
||||
Center = center;
|
||||
Width = Math.Max(width, 0d);
|
||||
Height = Math.Max(height, 0d);
|
||||
}
|
||||
|
||||
public override Location Center => center;
|
||||
public override double Width => width;
|
||||
public override double Height => height;
|
||||
public override Location Center { get; set; }
|
||||
public override double Width { get; }
|
||||
public override double Height { get; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,6 +31,10 @@ namespace MapControl
|
|||
/// </summary>
|
||||
public partial class MapBase : MapPanel
|
||||
{
|
||||
public static double ZoomLevelToScale(double zoomLevel) => 256d * Math.Pow(2d, zoomLevel) / (360d * MapProjection.Wgs84MeterPerDegree);
|
||||
|
||||
public static double ScaleToZoomLevel(double scale) => Math.Log(scale * 360d * MapProjection.Wgs84MeterPerDegree / 256d, 2d);
|
||||
|
||||
public static TimeSpan ImageFadeDuration { get; set; } = TimeSpan.FromSeconds(0.1);
|
||||
|
||||
public static readonly DependencyProperty AnimationDurationProperty =
|
||||
|
|
@ -361,7 +365,7 @@ namespace MapControl
|
|||
{
|
||||
var scale = Math.Min(ActualWidth / rect.Value.Width, ActualHeight / rect.Value.Height);
|
||||
|
||||
TargetZoomLevel = ViewTransform.ScaleToZoomLevel(scale);
|
||||
TargetZoomLevel = ScaleToZoomLevel(scale);
|
||||
TargetCenter = targetCenter;
|
||||
TargetHeading = 0d;
|
||||
}
|
||||
|
|
@ -497,7 +501,7 @@ namespace MapControl
|
|||
private void UpdateTransform(bool resetTransformCenter = false, bool projectionChanged = false)
|
||||
{
|
||||
var transformCenterChanged = false;
|
||||
var viewScale = ViewTransform.ZoomLevelToScale(ZoomLevel);
|
||||
var viewScale = ZoomLevelToScale(ZoomLevel);
|
||||
var projection = MapProjection;
|
||||
|
||||
projection.Center = ProjectionCenter ?? Center;
|
||||
|
|
|
|||
|
|
@ -189,66 +189,53 @@ namespace MapControl
|
|||
|
||||
protected Point? GetViewPosition(Location location)
|
||||
{
|
||||
var position = parentMap.LocationToView(location);
|
||||
|
||||
if (parentMap.MapProjection.Type <= MapProjectionType.NormalCylindrical &&
|
||||
position.HasValue &&
|
||||
!InsideViewport(position.Value))
|
||||
if (parentMap.MapProjection.Type <= MapProjectionType.NormalCylindrical)
|
||||
{
|
||||
position = parentMap.LocationToView(
|
||||
new Location(location.Latitude, parentMap.CoerceLongitude(location.Longitude)));
|
||||
var longitude = parentMap.CoerceLongitude(location.Longitude);
|
||||
|
||||
if (longitude != location.Longitude)
|
||||
{
|
||||
location = new Location(location.Latitude, longitude);
|
||||
}
|
||||
}
|
||||
|
||||
return position;
|
||||
return parentMap.LocationToView(location);
|
||||
}
|
||||
|
||||
protected ViewRect? GetViewRect(BoundingBox boundingBox)
|
||||
{
|
||||
var mapRect = parentMap.MapProjection.BoundingBoxToMap(boundingBox);
|
||||
|
||||
if (!mapRect.HasValue)
|
||||
if (parentMap.MapProjection.Type <= MapProjectionType.NormalCylindrical)
|
||||
{
|
||||
return null;
|
||||
var center = boundingBox.Center;
|
||||
var longitude = parentMap.CoerceLongitude(center.Longitude);
|
||||
|
||||
if (longitude != center.Longitude)
|
||||
{
|
||||
boundingBox.Center = new Location(center.Latitude, longitude);
|
||||
}
|
||||
}
|
||||
|
||||
var mapRect = parentMap.MapProjection.BoundingBoxToMap(boundingBox);
|
||||
|
||||
if (mapRect.HasValue)
|
||||
{
|
||||
return GetViewRect(mapRect.Value);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected ViewRect GetViewRect(Rect mapRect)
|
||||
{
|
||||
var transform = parentMap.ViewTransform;
|
||||
var center = new Point(mapRect.X + mapRect.Width / 2d, mapRect.Y + mapRect.Height / 2d);
|
||||
var position = parentMap.ViewTransform.MapToView(center);
|
||||
var projection = parentMap.MapProjection;
|
||||
|
||||
if (projection.Type <= MapProjectionType.NormalCylindrical &&
|
||||
!InsideViewport(position))
|
||||
{
|
||||
var location = projection.MapToLocation(center);
|
||||
|
||||
if (location != null)
|
||||
{
|
||||
var pos = parentMap.LocationToView(
|
||||
new Location(location.Latitude, parentMap.CoerceLongitude(location.Longitude)));
|
||||
|
||||
if (pos.HasValue)
|
||||
{
|
||||
position = pos.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var width = mapRect.Width * parentMap.ViewTransform.Scale;
|
||||
var height = mapRect.Height * parentMap.ViewTransform.Scale;
|
||||
var position = transform.MapToView(center);
|
||||
var width = mapRect.Width * transform.Scale;
|
||||
var height = mapRect.Height * transform.Scale;
|
||||
var x = position.X - width / 2d;
|
||||
var y = position.Y - height / 2d;
|
||||
|
||||
return new ViewRect(x, y, width, height, parentMap.ViewTransform.Rotation);
|
||||
}
|
||||
|
||||
private bool InsideViewport(Point point)
|
||||
{
|
||||
return point.X >= 0d && point.X <= parentMap.ActualWidth
|
||||
&& point.Y >= 0d && point.Y <= parentMap.ActualHeight;
|
||||
return new ViewRect(x, y, width, height, transform.Rotation);
|
||||
}
|
||||
|
||||
private void ArrangeChildElement(FrameworkElement element, Size panelSize)
|
||||
|
|
@ -263,7 +250,10 @@ namespace MapControl
|
|||
|
||||
if (GetAutoCollapse(element))
|
||||
{
|
||||
SetVisible(element, position.HasValue && InsideViewport(position.Value));
|
||||
SetVisible(element,
|
||||
position.HasValue &&
|
||||
position.Value.X >= 0d && position.Value.X <= parentMap.ActualWidth &&
|
||||
position.Value.Y >= 0d && position.Value.Y <= parentMap.ActualHeight);
|
||||
}
|
||||
|
||||
if (position.HasValue)
|
||||
|
|
|
|||
|
|
@ -4,13 +4,10 @@
|
|||
|
||||
#if WPF
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
#elif UWP
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Media;
|
||||
#elif WINUI
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Media;
|
||||
#endif
|
||||
|
||||
namespace MapControl
|
||||
|
|
@ -79,6 +76,18 @@ namespace MapControl
|
|||
MapPanel.SetLocation(this, Location);
|
||||
}
|
||||
|
||||
protected double GetLongitudeOffset(Location location)
|
||||
{
|
||||
var longitudeOffset = 0d;
|
||||
|
||||
if (location != null && parentMap.MapProjection.Type <= MapProjectionType.NormalCylindrical)
|
||||
{
|
||||
longitudeOffset = parentMap.CoerceLongitude(location.Longitude) - location.Longitude;
|
||||
}
|
||||
|
||||
return longitudeOffset;
|
||||
}
|
||||
|
||||
protected Point? LocationToMap(Location location, double longitudeOffset)
|
||||
{
|
||||
if (longitudeOffset != 0d)
|
||||
|
|
@ -107,31 +116,12 @@ namespace MapControl
|
|||
{
|
||||
var point = LocationToMap(location, longitudeOffset);
|
||||
|
||||
if (!point.HasValue)
|
||||
if (point.HasValue)
|
||||
{
|
||||
return null;
|
||||
point = parentMap.ViewTransform.MapToView(point.Value);
|
||||
}
|
||||
|
||||
return parentMap.ViewTransform.MapToView(point.Value);
|
||||
}
|
||||
|
||||
protected double GetLongitudeOffset(Location location)
|
||||
{
|
||||
var longitudeOffset = 0d;
|
||||
|
||||
if (location != null && parentMap.MapProjection.Type <= MapProjectionType.NormalCylindrical)
|
||||
{
|
||||
var point = parentMap.LocationToView(location);
|
||||
|
||||
if (point.HasValue &&
|
||||
(point.Value.X < 0d || point.Value.X > parentMap.ActualWidth ||
|
||||
point.Value.Y < 0d || point.Value.Y > parentMap.ActualHeight))
|
||||
{
|
||||
longitudeOffset = parentMap.CoerceLongitude(location.Longitude) - location.Longitude;
|
||||
}
|
||||
}
|
||||
|
||||
return longitudeOffset;
|
||||
return point;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -156,7 +156,7 @@ namespace MapControl
|
|||
//
|
||||
var tileMatrixOrigin = new Point(TileSize * TileMatrix.XMin, TileSize * TileMatrix.YMin);
|
||||
|
||||
var tileMatrixScale = ViewTransform.ZoomLevelToScale(TileMatrix.ZoomLevel);
|
||||
var tileMatrixScale = MapBase.ZoomLevelToScale(TileMatrix.ZoomLevel);
|
||||
|
||||
((MatrixTransform)RenderTransform).Matrix =
|
||||
ParentMap.ViewTransform.GetTileLayerTransform(tileMatrixScale, MapTopLeft, tileMatrixOrigin);
|
||||
|
|
@ -169,7 +169,7 @@ namespace MapControl
|
|||
//
|
||||
var tileMatrixZoomLevel = (int)Math.Floor(ParentMap.ZoomLevel - ZoomLevelOffset + 0.001);
|
||||
|
||||
var tileMatrixScale = ViewTransform.ZoomLevelToScale(tileMatrixZoomLevel);
|
||||
var tileMatrixScale = MapBase.ZoomLevelToScale(tileMatrixZoomLevel);
|
||||
|
||||
// Bounds in tile pixels from view size.
|
||||
//
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
// Copyright © 2024 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using System;
|
||||
#if WPF
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
|
|
@ -18,18 +17,8 @@ namespace MapControl
|
|||
/// Defines the transformation between projected map coordinates in meters
|
||||
/// and view coordinates in pixels.
|
||||
/// </summary>
|
||||
public class ViewTransform
|
||||
public partial class ViewTransform
|
||||
{
|
||||
public static double ZoomLevelToScale(double zoomLevel)
|
||||
{
|
||||
return 256d * Math.Pow(2d, zoomLevel) / (360d * MapProjection.Wgs84MeterPerDegree);
|
||||
}
|
||||
|
||||
public static double ScaleToZoomLevel(double scale)
|
||||
{
|
||||
return Math.Log(scale * 360d * MapProjection.Wgs84MeterPerDegree / 256d, 2d);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the scaling factor from projected map coordinates to view coordinates,
|
||||
/// as pixels per meter.
|
||||
|
|
@ -51,6 +40,32 @@ namespace MapControl
|
|||
/// </summary>
|
||||
public Matrix ViewToMapMatrix { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Transforms a Point from projected map coordinates to view coordinates.
|
||||
/// </summary>
|
||||
public Point MapToView(Point point)
|
||||
{
|
||||
return MapToViewMatrix.Transform(point);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Transforms a Point from view coordinates to projected map coordinates.
|
||||
/// </summary>
|
||||
public Point ViewToMap(Point point)
|
||||
{
|
||||
return ViewToMapMatrix.Transform(point);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Transform relative to absolute map scale. Returns horizontal and vertical
|
||||
/// scaling factors from meters to view coordinates.
|
||||
/// </summary>
|
||||
public Point GetMapScale(Point relativeScale)
|
||||
{
|
||||
return new Point(Scale * relativeScale.X, Scale * relativeScale.Y);
|
||||
}
|
||||
|
||||
#if WPF || UWP || WINUI
|
||||
/// <summary>
|
||||
/// Initializes a ViewTransform from a map center point in projected coordinates,
|
||||
/// a view conter point, a scaling factor from projected coordinates to view coordinates
|
||||
|
|
@ -72,30 +87,6 @@ namespace MapControl
|
|||
ViewToMapMatrix = transform;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Transforms a Point from projected map coordinates to view coordinates.
|
||||
/// </summary>
|
||||
public Point MapToView(Point point)
|
||||
{
|
||||
return MapToViewMatrix.Transform(point);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Transforms a Point from view coordinates to projected map coordinates.
|
||||
/// </summary>
|
||||
public Point ViewToMap(Point point)
|
||||
{
|
||||
return ViewToMapMatrix.Transform(point);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets scaling factors from meters to view coordinates for a relative map scale.
|
||||
/// </summary>
|
||||
public Point GetMapScale(Point relativeScale)
|
||||
{
|
||||
return new Point(Scale * relativeScale.X, Scale * relativeScale.Y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a transform Matrix from meters to view coordinates for a relative map scale.
|
||||
/// </summary>
|
||||
|
|
@ -170,5 +161,6 @@ namespace MapControl
|
|||
|
||||
return transform;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue