MapPanel longitude coercion

This commit is contained in:
ClemensFischer 2024-08-29 21:35:58 +02:00
parent 94c3faf805
commit 4b0a0d29fd
9 changed files with 110 additions and 189 deletions

View file

@ -12,7 +12,6 @@
<ItemGroup>
<Compile Include="..\Shared\*.cs" />
<Compile Remove="..\Shared\ViewTransform.cs" />
</ItemGroup>
<ItemGroup>

View file

@ -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>

View file

@ -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.

View file

@ -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; }
}
}

View file

@ -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;

View file

@ -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)

View file

@ -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;
}
}
}

View file

@ -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.
//

View file

@ -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
}
}