Reworked MapProjection

Return nullable Point from LocationToMap. Use MapRect instead of WinUI/UWP Rect replacement. Drop Vector. Add Scale struct.
This commit is contained in:
ClemensFischer 2022-12-02 16:50:10 +01:00
parent bab1788334
commit 218a85316c
28 changed files with 249 additions and 324 deletions

View file

@ -24,7 +24,7 @@ namespace MapControl
CrsId = DefaultCrsId;
}
public override Point LocationToMap(Location location)
public override Point? LocationToMap(Location location)
{
return new Point(
Wgs84MeterPerDegree * (location.Longitude - Center.Longitude) * Math.Cos(Center.Latitude * Math.PI / 180d),

View file

@ -20,7 +20,7 @@ namespace MapControl
CrsId = crsId;
}
public override Point LocationToMap(Location location)
public override Point? LocationToMap(Location location)
{
if (location.Equals(Center))
{

View file

@ -19,22 +19,34 @@ namespace MapControl
Type = MapProjectionType.Azimuthal;
}
public override Rect BoundingBoxToRect(BoundingBox boundingBox)
public override MapRect BoundingBoxToMapRect(BoundingBox boundingBox)
{
var center = LocationToMap(boundingBox.Center);
if (!center.HasValue)
{
return null;
}
var width = boundingBox.Width * Wgs84MeterPerDegree;
var height = boundingBox.Height * Wgs84MeterPerDegree;
return new Rect(center.X - width / 2d, center.Y - height / 2d, width, height);
return new MapRect(center.Value.X - width / 2d, center.Value.Y - height / 2d, width, height);
}
public override BoundingBox RectToBoundingBox(Rect rect)
public override BoundingBox MapRectToBoundingBox(MapRect rect)
{
var center = MapToLocation(new Point(rect.X + rect.Width / 2d, rect.Y + rect.Height / 2d));
if (center == null)
{
return null;
}
var width = rect.Width / Wgs84MeterPerDegree;
var height = rect.Height / Wgs84MeterPerDegree;
return center != null ? new CenteredBoundingBox(center, width, height) : null;
return new CenteredBoundingBox(center, width, height);
}
/// <summary>

View file

@ -25,14 +25,14 @@ namespace MapControl
CrsId = DefaultCrsId;
}
public override Vector GetRelativeScale(Location location)
public override Scale GetRelativeScale(Location location)
{
return new Vector(
return new Scale(
1d / Math.Cos(location.Latitude * Math.PI / 180d),
1d);
}
public override Point LocationToMap(Location location)
public override Point? LocationToMap(Location location)
{
return new Point(
Wgs84MeterPerDegree * location.Longitude,
@ -46,7 +46,7 @@ namespace MapControl
point.X / Wgs84MeterPerDegree);
}
public override string GetBboxValue(Rect rect)
public override string GetBboxValue(MapRect rect)
{
return string.Format(CultureInfo.InvariantCulture,
CrsId == "CRS:84" ? "{0},{1},{2},{3}" : "{1},{0},{3},{2}",

View file

@ -100,7 +100,7 @@ namespace MapControl
transform.M21 = 0;
}
var rect = new Rect(
var rect = new MapRect(
transform.Transform(new Point()),
transform.Transform(new Point(bitmap.PixelWidth, bitmap.PixelHeight)));

View file

@ -22,7 +22,7 @@ namespace MapControl
CrsId = DefaultCrsId;
}
public override Point LocationToMap(Location location)
public override Point? LocationToMap(Location location)
{
if (location.Equals(Center))
{

View file

@ -3,7 +3,9 @@
// Licensed under the Microsoft Public License (Ms-PL)
using System;
#if !WINUI && !UWP
#if WINUI || UWP
using Windows.Foundation;
#else
using System.Windows;
#endif
@ -48,30 +50,30 @@ namespace MapControl
return true;
}
var topLeft = new Point(rect.X, rect.Y);
var topRight = new Point(rect.X + rect.Width, rect.Y);
var bottomLeft = new Point(rect.X, rect.Y + rect.Height);
var bottomRight = new Point(rect.X + rect.Width, rect.Y + rect.Height);
var topLeft = new Point(rect.Left, rect.Top);
var topRight = new Point(rect.Right, rect.Top);
var bottomLeft = new Point(rect.Left, rect.Bottom);
var bottomRight = new Point(rect.Right, rect.Bottom);
var numIntersections = 0;
if (GetIntersection(ref p1, ref p2, topLeft, bottomLeft, p => p.X <= rect.X)) // left edge
if (GetIntersection(ref p1, ref p2, topLeft, bottomLeft, p => p.X <= rect.Left)) // left edge
{
numIntersections++;
}
if (GetIntersection(ref p1, ref p2, topLeft, topRight, p => p.Y <= rect.Y)) // top edge
if (GetIntersection(ref p1, ref p2, topLeft, topRight, p => p.Y <= rect.Top)) // top edge
{
numIntersections++;
}
if (numIntersections < 2 &&
GetIntersection(ref p1, ref p2, topRight, bottomRight, p => p.X >= rect.X + rect.Width)) // right edge
GetIntersection(ref p1, ref p2, topRight, bottomRight, p => p.X >= rect.Right)) // right edge
{
numIntersections++;
}
if (numIntersections < 2 &&
GetIntersection(ref p1, ref p2, bottomLeft, bottomRight, p => p.Y >= rect.Y + rect.Height)) // bottom edge
GetIntersection(ref p1, ref p2, bottomLeft, bottomRight, p => p.Y >= rect.Bottom)) // bottom edge
{
numIntersections++;
}

View file

@ -4,10 +4,12 @@
using System;
#if WINUI
using Windows.Foundation;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Media.Animation;
#elif UWP
using Windows.Foundation;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Media.Animation;
@ -229,7 +231,7 @@ namespace MapControl
/// Gets the map scale as the horizontal and vertical scaling factors from geographic
/// coordinates to view coordinates at the specified location, as pixels per meter.
/// </summary>
public Vector GetScale(Location location)
public Scale GetScale(Location location)
{
return ViewTransform.Scale * MapProjection.GetRelativeScale(location);
}
@ -237,9 +239,16 @@ namespace MapControl
/// <summary>
/// Transforms a Location in geographic coordinates to a Point in view coordinates.
/// </summary>
public Point LocationToView(Location location)
public Point? LocationToView(Location location)
{
return ViewTransform.MapToView(MapProjection.LocationToMap(location));
var point = MapProjection.LocationToMap(location);
if (!point.HasValue)
{
return null;
}
return ViewTransform.MapToView(point.Value);
}
/// <summary>
@ -265,7 +274,7 @@ namespace MapControl
var y1 = Math.Min(p1.Y, Math.Min(p2.Y, Math.Min(p3.Y, p4.Y)));
var y2 = Math.Max(p1.Y, Math.Max(p2.Y, Math.Max(p3.Y, p4.Y)));
return MapProjection.RectToBoundingBox(new Rect(x1, y1, x2 - x1, y2 - y1));
return MapProjection.MapRectToBoundingBox(new MapRect(x1, y1, x2 - x1, y2 - y1));
}
/// <summary>
@ -291,7 +300,7 @@ namespace MapControl
/// <summary>
/// Changes the Center property according to the specified translation in view coordinates.
/// </summary>
public void TranslateMap(Vector translation)
public void TranslateMap(Point translation)
{
if (transformCenter != null)
{
@ -301,7 +310,8 @@ namespace MapControl
if (translation.X != 0d || translation.Y != 0d)
{
var center = ViewToLocation(viewCenter - translation);
var center = ViewToLocation(new Point(viewCenter.X - translation.X, viewCenter.Y - translation.Y));
if (center != null)
{
Center = center;
@ -314,12 +324,14 @@ namespace MapControl
/// 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)
public void TransformMap(Point center, Point translation, double rotation, double scale)
{
if (rotation != 0d || scale != 1d)
{
SetTransformCenter(center);
viewCenter += translation;
viewCenter.X += translation.X;
viewCenter.Y += translation.Y;
if (rotation != 0d)
{
@ -368,7 +380,7 @@ namespace MapControl
/// </summary>
public void ZoomToBounds(BoundingBox boundingBox)
{
var rect = MapProjection.BoundingBoxToRect(boundingBox);
var rect = MapProjection.BoundingBoxToMapRect(boundingBox);
var center = new Point(rect.X + rect.Width / 2d, rect.Y + rect.Height / 2d);
var targetCenter = MapProjection.MapToLocation(center);
@ -737,9 +749,9 @@ namespace MapControl
var mapCenter = projection.LocationToMap(transformCenter ?? Center);
if (MapProjection.IsValid(mapCenter))
if (mapCenter.HasValue)
{
ViewTransform.SetTransform(mapCenter, viewCenter, viewScale, -Heading);
ViewTransform.SetTransform(mapCenter.Value, viewCenter, viewScale, -Heading);
if (transformCenter != null)
{
@ -774,9 +786,9 @@ namespace MapControl
mapCenter = projection.LocationToMap(center);
if (MapProjection.IsValid(mapCenter))
if (mapCenter.HasValue)
{
ViewTransform.SetTransform(mapCenter, viewCenter, viewScale, -Heading);
ViewTransform.SetTransform(mapCenter.Value, viewCenter, viewScale, -Heading);
}
}
}

View file

@ -7,9 +7,11 @@ using System.Collections.Generic;
using System.Globalization;
using System.Linq;
#if WINUI
using Windows.Foundation;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Media;
#elif UWP
using Windows.Foundation;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Media;
#else
@ -104,8 +106,7 @@ namespace MapControl
private void AddLabel(ICollection<Label> labels, Location location, Point position, double? rotation = null)
{
if (MapProjection.IsValid(position) &&
position.X >= 0d && position.X <= ParentMap.RenderSize.Width &&
if (position.X >= 0d && position.X <= ParentMap.RenderSize.Width &&
position.Y >= 0d && position.Y <= ParentMap.RenderSize.Height)
{
if (!rotation.HasValue)
@ -115,9 +116,9 @@ namespace MapControl
var pos = ParentMap.LocationToView(
new Location(location.Latitude, location.Longitude + 10d / PixelPerLongitudeDegree(location)));
if (MapProjection.IsValid(pos))
if (pos.HasValue)
{
rotation = Math.Atan2(pos.Y - position.Y, pos.X - position.X) * 180d / Math.PI;
rotation = Math.Atan2(pos.Value.Y - position.Y, pos.Value.X - position.X) * 180d / Math.PI;
}
}
@ -162,9 +163,9 @@ namespace MapControl
var p1 = ParentMap.LocationToView(new Location(lat, bounds.West));
var p2 = ParentMap.LocationToView(new Location(lat, bounds.East));
if (MapProjection.IsValid(p1) && MapProjection.IsValid(p2))
if (p1.HasValue && p2.HasValue)
{
figures.Add(CreateLineFigure(p1, p2));
figures.Add(CreateLineFigure(p1.Value, p2.Value));
}
}
@ -173,9 +174,9 @@ namespace MapControl
var p1 = ParentMap.LocationToView(new Location(bounds.South, lon));
var p2 = ParentMap.LocationToView(new Location(bounds.North, lon));
if (MapProjection.IsValid(p1) && MapProjection.IsValid(p2))
if (p1.HasValue && p2.HasValue)
{
figures.Add(CreateLineFigure(p1, p2));
figures.Add(CreateLineFigure(p1.Value, p2.Value));
}
for (var lat = latLabelStart; lat <= bounds.North; lat += lineDistance)
@ -183,7 +184,10 @@ namespace MapControl
var location = new Location(lat, lon);
var position = ParentMap.LocationToView(location);
AddLabel(labels, location, position, ParentMap.ViewTransform.Rotation);
if (position.HasValue)
{
AddLabel(labels, location, position.Value, ParentMap.ViewTransform.Rotation);
}
}
}
}
@ -240,10 +244,10 @@ namespace MapControl
var points = new List<Point>();
var position = ParentMap.LocationToView(location);
if (MapProjection.IsValid(position))
if (position.HasValue)
{
points.Add(position);
AddLabel(labels, location, position);
points.Add(position.Value);
AddLabel(labels, location, position.Value);
}
for (int j = 0; j < lonSegments; j++)
@ -254,13 +258,16 @@ namespace MapControl
location = new Location(lat, lon);
position = ParentMap.LocationToView(location);
if (MapProjection.IsValid(position))
if (position.HasValue)
{
points.Add(position);
points.Add(position.Value);
}
}
AddLabel(labels, location, position);
if (position.HasValue)
{
AddLabel(labels, location, position.Value);
}
}
if (points.Count >= 2)
@ -280,13 +287,13 @@ namespace MapControl
{
var p = ParentMap.LocationToView(new Location(startLatitude + i * deltaLatitude, longitude));
if (MapProjection.IsValid(p))
if (p.HasValue)
{
visible = visible ||
p.X >= 0d && p.X <= ParentMap.RenderSize.Width &&
p.Y >= 0d && p.Y <= ParentMap.RenderSize.Height;
p.Value.X >= 0d && p.Value.X <= ParentMap.RenderSize.Width &&
p.Value.Y >= 0d && p.Value.Y <= ParentMap.RenderSize.Height;
points.Add(p);
points.Add(p.Value);
}
}
@ -305,12 +312,16 @@ namespace MapControl
var northPole = ParentMap.LocationToView(new Location(90d, 0d));
var southPole = ParentMap.LocationToView(new Location(-90d, 0d));
if (northPole.X >= 0d && northPole.Y >= 0d && northPole.X <= width && northPole.Y <= height)
if (northPole.HasValue &&
northPole.Value.X >= 0d && northPole.Value.X <= width &&
northPole.Value.Y >= 0d && northPole.Value.Y <= height)
{
maxLatitude = 90d;
}
if (southPole.X >= 0d && southPole.Y >= 0d && southPole.X <= width && southPole.Y <= height)
if (southPole.HasValue &&
southPole.Value.X >= 0d && southPole.Value.X <= width &&
southPole.Value.Y >= 0d && southPole.Value.Y <= height)
{
minLatitude = -90d;
}

View file

@ -7,12 +7,14 @@ using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
#if WINUI
using Windows.Foundation;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Media.Animation;
using DispatcherTimer = Microsoft.UI.Dispatching.DispatcherQueueTimer;
#elif UWP
using Windows.Foundation;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;

View file

@ -203,12 +203,13 @@ namespace MapControl
return finalSize;
}
protected Point GetViewPosition(Location location)
protected Point? GetViewPosition(Location location)
{
var position = parentMap.LocationToView(location);
if (parentMap.MapProjection.Type <= MapProjectionType.NormalCylindrical &&
IsOutsideViewport(position))
position.HasValue &&
IsOutsideViewport(position.Value))
{
location = new Location(location.Latitude, parentMap.ConstrainedLongitude(location.Longitude));
position = parentMap.LocationToView(location);
@ -219,10 +220,10 @@ namespace MapControl
protected ViewRect GetViewRect(BoundingBox boundingBox)
{
return GetViewRect(parentMap.MapProjection.BoundingBoxToRect(boundingBox));
return GetViewRect(parentMap.MapProjection.BoundingBoxToMapRect(boundingBox));
}
protected ViewRect GetViewRect(Rect rect)
protected ViewRect GetViewRect(MapRect rect)
{
var center = new Point(rect.X + rect.Width / 2d, rect.Y + rect.Height / 2d);
var position = parentMap.ViewTransform.MapToView(center);
@ -235,7 +236,12 @@ namespace MapControl
if (location != null)
{
location.Longitude = parentMap.ConstrainedLongitude(location.Longitude);
position = parentMap.LocationToView(location);
var pos = parentMap.LocationToView(location);
if (pos.HasValue)
{
position = pos.Value;
}
}
}

View file

@ -95,10 +95,11 @@ namespace MapControl
if (location != null && parentMap.MapProjection.Type <= MapProjectionType.NormalCylindrical)
{
var pos = parentMap.LocationToView(location);
var point = parentMap.LocationToView(location);
if (pos.X < 0d || pos.X > parentMap.RenderSize.Width ||
pos.Y < 0d || pos.Y > parentMap.RenderSize.Height)
if (point.HasValue &&
(point.Value.X < 0d || point.Value.X > parentMap.RenderSize.Width ||
point.Value.Y < 0d || point.Value.Y > parentMap.RenderSize.Height))
{
longitudeOffset = parentMap.ConstrainedLongitude(location.Longitude) - location.Longitude;
}
@ -107,7 +108,7 @@ namespace MapControl
return longitudeOffset;
}
protected Point LocationToMap(Location location, double longitudeOffset)
protected Point? LocationToMap(Location location, double longitudeOffset)
{
if (longitudeOffset != 0d)
{
@ -116,21 +117,24 @@ namespace MapControl
var point = parentMap.MapProjection.LocationToMap(location);
if (point.Y == double.PositiveInfinity)
if (point.HasValue && double.IsInfinity(point.Value.Y))
{
point.Y = 1e9;
}
else if (point.X == double.NegativeInfinity)
{
point.Y = -1e9;
point = null;
}
return point;
}
protected Point LocationToView(Location location, double longitudeOffset)
protected Point? LocationToView(Location location, double longitudeOffset)
{
return parentMap.ViewTransform.MapToView(LocationToMap(location, longitudeOffset));
var point = LocationToMap(location, longitudeOffset);
if (!point.HasValue)
{
return null;
}
return parentMap.ViewTransform.MapToView(point.Value);
}
#endregion

View file

@ -49,16 +49,16 @@ namespace MapControl
/// <summary>
/// Gets the relative map scale at the specified Location.
/// </summary>
public virtual Vector GetRelativeScale(Location location)
public virtual Scale GetRelativeScale(Location location)
{
return new Vector(1d, 1d);
return new Scale(1d, 1d);
}
/// <summary>
/// Transforms a Location in geographic coordinates to a Point in projected map coordinates.
/// Returns new Point(double.NaN, double.NaN) when the Location can not be transformed.
/// Returns null when the Location can not be transformed.
/// </summary>
public abstract Point LocationToMap(Location location);
public abstract Point? LocationToMap(Location location);
/// <summary>
/// Transforms a Point in projected map coordinates to a Location in geographic coordinates.
@ -67,20 +67,22 @@ namespace MapControl
public abstract Location MapToLocation(Point point);
/// <summary>
/// Transforms a BoundingBox in geographic coordinates to a Rect in projected map coordinates.
/// Transforms a BoundingBox in geographic coordinates to a MapRect in projected map coordinates.
/// Returns null when the BoundingBox can not be transformed.
/// </summary>
public virtual Rect BoundingBoxToRect(BoundingBox boundingBox)
public virtual MapRect BoundingBoxToMapRect(BoundingBox boundingBox)
{
return new Rect(
LocationToMap(new Location(boundingBox.South, boundingBox.West)),
LocationToMap(new Location(boundingBox.North, boundingBox.East)));
var p1 = LocationToMap(new Location(boundingBox.South, boundingBox.West));
var p2 = LocationToMap(new Location(boundingBox.North, boundingBox.East));
return p1.HasValue && p2.HasValue ? new MapRect(p1.Value, p2.Value) : null;
}
/// <summary>
/// Transforms a Rect in projected map coordinates to a BoundingBox in geographic coordinates.
/// Returns null when the Rect can not be transformed.
/// Transforms a MapRect in projected map coordinates to a BoundingBox in geographic coordinates.
/// Returns null when the MapRect can not be transformed.
/// </summary>
public virtual BoundingBox RectToBoundingBox(Rect rect)
public virtual BoundingBox MapRectToBoundingBox(MapRect rect)
{
var sw = MapToLocation(new Point(rect.X, rect.Y));
var ne = MapToLocation(new Point(rect.X + rect.Width, rect.Y + rect.Height));
@ -103,30 +105,10 @@ namespace MapControl
/// <summary>
/// Gets the BBOX parameter value for a WMS GetMap request.
/// </summary>
public virtual string GetBboxValue(Rect rect)
public virtual string GetBboxValue(MapRect rect)
{
return string.Format(CultureInfo.InvariantCulture,
"{0},{1},{2},{3}", rect.X, rect.Y, (rect.X + rect.Width), (rect.Y + rect.Height));
}
/// <summary>
/// Checks if the X and Y values of a Point are neither NaN nor Infinity.
/// </summary>
public static bool IsValid(Point point)
{
return !double.IsNaN(point.X) && !double.IsInfinity(point.X) &&
!double.IsNaN(point.Y) && !double.IsInfinity(point.Y);
}
/// <summary>
/// Checks if the X, Y, Width and Height values of a Rect are neither NaN nor Infinity.
/// </summary>
public static bool IsValid(Rect rect)
{
return !double.IsNaN(rect.X) && !double.IsInfinity(rect.X) &&
!double.IsNaN(rect.Y) && !double.IsInfinity(rect.Y) &&
!double.IsNaN(rect.Width) && !double.IsInfinity(rect.Width) &&
!double.IsNaN(rect.Height) && !double.IsInfinity(rect.Height);
}
}
}

View file

@ -0,0 +1,45 @@
// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2022 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL)
using System;
#if !WINUI && !UWP
using System.Windows;
#endif
namespace MapControl
{
/// <summary>
/// Map rectangle with double floating point precision, unlike Windows.Foundation.Rect does.
/// Used by MapProjection to convert geodetic bounding boxes to/from projected map coordinates.
/// </summary>
public class MapRect
{
public MapRect(double x, double y, double width, double height)
{
X = x;
Y = y;
Width = width;
Height = height;
}
public MapRect(Point p1, Point p2)
{
X = Math.Min(p1.X, p2.X);
Y = Math.Min(p1.Y, p2.Y);
Width = Math.Max(p1.X, p2.X) - X;
Height = Math.Max(p1.Y, p2.Y) - Y;
}
public double X { get; }
public double Y { get; }
public double Width { get; }
public double Height { get; }
public bool Contains(Point p)
{
return p.X >= X && p.X <= X + Width
&& p.Y >= Y && p.Y <= Y + Height;
}
}
}

View file

@ -22,7 +22,7 @@ namespace MapControl
CrsId = DefaultCrsId;
}
public override Point LocationToMap(Location location)
public override Point? LocationToMap(Location location)
{
if (location.Equals(Center))
{
@ -35,7 +35,7 @@ namespace MapControl
if (Math.Abs(lat - lat0) > Math.PI / 2d || Math.Abs(dLon) > Math.PI / 2d)
{
return new Point(double.NaN, double.NaN);
return null;
}
return new Point(

View file

@ -0,0 +1,23 @@
// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2022 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL)
namespace MapControl
{
public struct Scale
{
public Scale(double x, double y)
{
X = x;
Y = y;
}
public double X { get; set; }
public double Y { get; set; }
public static Scale operator *(double f, Scale v)
{
return new Scale(f * v.X, f * v.Y);
}
}
}

View file

@ -22,7 +22,7 @@ namespace MapControl
CrsId = DefaultCrsId;
}
public override Point LocationToMap(Location location)
public override Point? LocationToMap(Location location)
{
if (location.Equals(Center))
{

View file

@ -23,14 +23,14 @@ namespace MapControl
CrsId = DefaultCrsId;
}
public override Vector GetRelativeScale(Location location)
public override Scale GetRelativeScale(Location location)
{
var k = 1d / Math.Cos(location.Latitude * Math.PI / 180d); // p.44 (7-3)
return new Vector(k, k);
return new Scale(k, k);
}
public override Point LocationToMap(Location location)
public override Point? LocationToMap(Location location)
{
return new Point(
Wgs84MeterPerDegree * location.Longitude,

View file

@ -9,9 +9,11 @@ using System.Linq;
using System.Threading.Tasks;
using System.Xml.Linq;
#if WINUI
using Windows.Foundation;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Media;
#elif UWP
using Windows.Foundation;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Media;
#else
@ -237,7 +239,7 @@ namespace MapControl
/// </summary>
protected virtual string GetMapRequestUri(BoundingBox boundingBox)
{
var mapRect = ParentMap.MapProjection.BoundingBoxToRect(boundingBox);
var mapRect = ParentMap.MapProjection.BoundingBoxToMapRect(boundingBox);
var viewScale = ParentMap.ViewTransform.Scale;
return GetRequestUri(new Dictionary<string, string>
@ -262,7 +264,7 @@ namespace MapControl
{
var viewSize = ParentMap.RenderSize;
var boundingBox = ParentMap.ViewRectToBoundingBox(new Rect(0d, 0d, viewSize.Width, viewSize.Height));
var mapRect = ParentMap.MapProjection.BoundingBoxToRect(boundingBox);
var mapRect = ParentMap.MapProjection.BoundingBoxToMapRect(boundingBox);
var viewRect = GetViewRect(mapRect);
var transform = new Matrix(1, 0, 0, 1, -viewSize.Width / 2, -viewSize.Height / 2);
@ -296,7 +298,7 @@ namespace MapControl
return ParentMap.MapProjection.GetCrsValue();
}
protected virtual string GetBboxValue(Rect mapRect)
protected virtual string GetBboxValue(MapRect mapRect)
{
return ParentMap.MapProjection.GetBboxValue(mapRect);
}

View file

@ -26,16 +26,16 @@ namespace MapControl
CrsId = DefaultCrsId;
}
public override Vector GetRelativeScale(Location location)
public override Scale GetRelativeScale(Location location)
{
var lat = location.Latitude * Math.PI / 180d;
var eSinLat = Wgs84Eccentricity * Math.Sin(lat);
var k = Math.Sqrt(1d - eSinLat * eSinLat) / Math.Cos(lat); // p.44 (7-8)
return new Vector(k, k);
return new Scale(k, k);
}
public override Point LocationToMap(Location location)
public override Point? LocationToMap(Location location)
{
return new Point(
Wgs84MeterPerDegree * location.Longitude,

View file

@ -128,6 +128,9 @@
<Compile Include="..\Shared\MapProjectionFactory.cs">
<Link>MapProjectionFactory.cs</Link>
</Compile>
<Compile Include="..\Shared\MapRect.cs">
<Link>MapRect.cs</Link>
</Compile>
<Compile Include="..\Shared\MapScale.cs">
<Link>MapScale.cs</Link>
</Compile>
@ -143,6 +146,9 @@
<Compile Include="..\Shared\PushpinBorder.cs">
<Link>PushpinBorder.cs</Link>
</Compile>
<Compile Include="..\Shared\Scale.cs">
<Link>Scale.cs</Link>
</Compile>
<Compile Include="..\Shared\StereographicProjection.cs">
<Link>StereographicProjection.cs</Link>
</Compile>
@ -242,15 +248,9 @@
<Compile Include="..\WinUI\PushpinBorder.WinUI.cs">
<Link>PushpinBorder.WinUI.cs</Link>
</Compile>
<Compile Include="..\WinUI\Rect.WinUI.cs">
<Link>Rect.WinUI.cs</Link>
</Compile>
<Compile Include="..\WinUI\Tile.WinUI.cs">
<Link>Tile.WinUI.cs</Link>
</Compile>
<Compile Include="..\WinUI\Vector.WinUI.cs">
<Link>Vector.WinUI.cs</Link>
</Compile>
<Compile Include="..\WPF\Timer.WPF.cs">
<Link>Timer.WPF.cs</Link>
</Compile>

View file

@ -65,7 +65,7 @@ namespace MapControl
private void OnManipulationDelta(object sender, ManipulationDeltaEventArgs e)
{
TransformMap(e.ManipulationOrigin,
e.DeltaManipulation.Translation,
(Point)e.DeltaManipulation.Translation,
e.DeltaManipulation.Rotation,
e.DeltaManipulation.Scale.LengthSquared / 2d);
}
@ -95,7 +95,7 @@ namespace MapControl
var translation = position - mousePosition.Value;
mousePosition = position;
TranslateMap(translation);
TranslateMap((Point)translation);
}
}

View file

@ -15,7 +15,7 @@ namespace MapControl
{
IEnumerable<Location> Locations { get; }
Drawing GetDrawing(IList<Point> positions, double scale, double rotation);
Drawing GetDrawing(IList<Point> points, double scale, double rotation);
}
public class MapItemsImageLayer : MapImageLayer
@ -47,23 +47,27 @@ namespace MapControl
{
var scale = ParentMap.ViewTransform.Scale;
var rotation = ParentMap.ViewTransform.Rotation;
var mapRect = projection.BoundingBoxToRect(boundingBox);
var mapRect = projection.BoundingBoxToMapRect(boundingBox);
var drawings = new DrawingGroup();
foreach (var item in items)
{
var positions = item.Locations.Select(l => projection.LocationToMap(l)).ToList();
var points = item.Locations
.Select(location => projection.LocationToMap(location))
.Where(point => point.HasValue)
.Select(point => point.Value)
.ToList();
if (positions.Any(p => mapRect.Contains(p)))
if (points.Any(point => mapRect.Contains(point)))
{
for (int i = 0; i < positions.Count; i++)
for (int i = 0; i < points.Count; i++)
{
positions[i] = new Point(
scale * (positions[i].X - mapRect.X),
scale * (mapRect.Height + mapRect.Y - positions[i].Y));
points[i] = new Point(
scale * (points[i].X - mapRect.X),
scale * (mapRect.Height + mapRect.Y - points[i].Y));
}
drawings.Children.Add(item.GetDrawing(positions, scale, rotation));
drawings.Children.Add(item.GetDrawing(points, scale, rotation));
}
}

View file

@ -73,7 +73,11 @@ namespace MapControl
{
if (locations.Count() >= 2)
{
var points = locations.Select(location => LocationToView(location, longitudeOffset));
var points = locations
.Select(location => LocationToView(location, longitudeOffset))
.Where(point => point.HasValue)
.Select(point => point.Value);
var figure = new PathFigure
{
StartPoint = points.First(),

View file

@ -5,6 +5,7 @@
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using Windows.Foundation;
#if WINUI
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Media;
@ -50,7 +51,10 @@ namespace MapControl
{
if (locations.Count() >= 2)
{
var points = locations.Select(location => LocationToView(location, longitudeOffset));
var points = locations
.Select(location => LocationToView(location, longitudeOffset))
.Where(point => point.HasValue)
.Select(point => point.Value);
if (closed)
{

View file

@ -28,31 +28,6 @@ namespace MapControl
return new Point(p.X, p.Y);
}
public static explicit operator Point(Vector v)
{
return new Point(v.X, v.Y);
}
public static Point operator -(Point p)
{
return new Point(-p.X, -p.Y);
}
public static Point operator +(Point p, Vector v)
{
return new Point(p.X + v.X, p.Y + v.Y);
}
public static Point operator -(Point p, Vector v)
{
return new Point(p.X - v.X, p.Y - v.Y);
}
public static Vector operator -(Point p1, Point p2)
{
return new Vector(p1.X - p2.X, p1.Y - p2.Y);
}
public static bool operator ==(Point p1, Point p2)
{
return p1.X == p2.X && p1.Y == p2.Y;

View file

@ -1,80 +0,0 @@
// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2022 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL)
using System;
namespace MapControl
{
/// <summary>
/// Replaces Windows.Foundation.Rect for double floating point precision.
/// </summary>
public struct Rect
{
public Rect(double x, double y, double width, double height)
{
X = x;
Y = y;
Width = width;
Height = height;
}
public Rect(Point p, Windows.Foundation.Size s)
{
X = p.X;
Y = p.Y;
Width = s.Width;
Height = s.Height;
}
public Rect(Point p1, Point p2)
{
X = Math.Min(p1.X, p2.X);
Y = Math.Min(p1.Y, p2.Y);
Width = Math.Max(p1.X, p2.X) - X;
Height = Math.Max(p1.Y, p2.Y) - Y;
}
public double X { get; set; }
public double Y { get; set; }
public double Width { get; set; }
public double Height { get; set; }
public bool Contains(Point p)
{
return p.X >= X && p.X <= X + Width
&& p.Y >= Y && p.Y <= Y + Height;
}
public static implicit operator Windows.Foundation.Rect(Rect r)
{
return new Windows.Foundation.Rect(r.X, r.Y, r.Width, r.Height);
}
public static implicit operator Rect(Windows.Foundation.Rect r)
{
return new Rect(r.X, r.Y, r.Width, r.Height);
}
public static bool operator ==(Rect r1, Rect r2)
{
return r1.X == r2.X && r1.Y == r2.Y
&& r1.Width == r2.Width && r1.Height == r2.Height;
}
public static bool operator !=(Rect r1, Rect r2)
{
return !(r1 == r2);
}
public override bool Equals(object obj)
{
return obj is Rect r && this == r;
}
public override int GetHashCode()
{
return X.GetHashCode() ^ Y.GetHashCode() ^ Width.GetHashCode() ^ Height.GetHashCode();
}
}
}

View file

@ -1,83 +0,0 @@
// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
// © 2022 Clemens Fischer
// Licensed under the Microsoft Public License (Ms-PL)
namespace MapControl
{
public struct Vector
{
public Vector(double x, double y)
{
X = x;
Y = y;
}
public double X { get; set; }
public double Y { get; set; }
public static implicit operator Windows.Foundation.Point(Vector v)
{
return new Windows.Foundation.Point(v.X, v.Y);
}
public static implicit operator Vector(Windows.Foundation.Point v)
{
return new Vector(v.X, v.Y);
}
public static explicit operator Vector(Point p)
{
return new Vector(p.X, p.Y);
}
public static Vector operator -(Vector v)
{
return new Vector(-v.X, -v.Y);
}
public static Point operator +(Vector v, Point p)
{
return new Point(v.X + p.X, v.Y + p.Y);
}
public static Vector operator +(Vector v1, Vector v2)
{
return new Vector(v1.X + v2.X, v1.Y + v2.Y);
}
public static Vector operator -(Vector v1, Vector v2)
{
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;
}
public static bool operator !=(Vector v1, Vector v2)
{
return !(v1 == v2);
}
public override bool Equals(object obj)
{
return obj is Vector v && this == v;
}
public override int GetHashCode()
{
return X.GetHashCode() ^ Y.GetHashCode();
}
}
}