mirror of
https://github.com/ClemensFischer/XAML-Map-Control.git
synced 2025-12-06 07:12:04 +01:00
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:
parent
bab1788334
commit
218a85316c
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ namespace MapControl
|
|||
CrsId = crsId;
|
||||
}
|
||||
|
||||
public override Point LocationToMap(Location location)
|
||||
public override Point? LocationToMap(Location location)
|
||||
{
|
||||
if (location.Equals(Center))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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}",
|
||||
|
|
|
|||
|
|
@ -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)));
|
||||
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ namespace MapControl
|
|||
CrsId = DefaultCrsId;
|
||||
}
|
||||
|
||||
public override Point LocationToMap(Location location)
|
||||
public override Point? LocationToMap(Location location)
|
||||
{
|
||||
if (location.Equals(Center))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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++;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
45
MapControl/Shared/MapRect.cs
Normal file
45
MapControl/Shared/MapRect.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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(
|
||||
|
|
|
|||
23
MapControl/Shared/Scale.cs
Normal file
23
MapControl/Shared/Scale.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -22,7 +22,7 @@ namespace MapControl
|
|||
CrsId = DefaultCrsId;
|
||||
}
|
||||
|
||||
public override Point LocationToMap(Location location)
|
||||
public override Point? LocationToMap(Location location)
|
||||
{
|
||||
if (location.Equals(Center))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue