mirror of
https://github.com/ClemensFischer/XAML-Map-Control.git
synced 2026-01-31 04:44:14 +01:00
Removed azimuthal and "auto" map projections
Removing these projections - which were never really well implemented - greatly simplifies the code. There is no ProjectionCenter anymore and MapProjection methods do not return null Locations or Points.
This commit is contained in:
parent
f4481c31f0
commit
76b879dfac
|
|
@ -13,8 +13,8 @@ namespace MapControl
|
|||
public static readonly AttachedProperty<BoundingBox> BoundingBoxProperty =
|
||||
DependencyPropertyHelper.RegisterAttached<BoundingBox>("BoundingBox", typeof(MapPanel));
|
||||
|
||||
public static readonly AttachedProperty<MapRect> MapRectProperty =
|
||||
DependencyPropertyHelper.RegisterAttached<MapRect>("MapRect", typeof(MapPanel));
|
||||
public static readonly AttachedProperty<Rect?> MapRectProperty =
|
||||
DependencyPropertyHelper.RegisterAttached<Rect?>("MapRect", typeof(MapPanel));
|
||||
|
||||
static MapPanel()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -41,10 +41,7 @@ namespace MapControl
|
|||
|
||||
private void AddPolylinePoints(PathFigures figures, IEnumerable<Location> locations, double longitudeOffset, bool closed)
|
||||
{
|
||||
var points = locations
|
||||
.Select(location => LocationToView(location, longitudeOffset))
|
||||
.Where(point => point.HasValue)
|
||||
.Select(point => point.Value);
|
||||
var points = locations.Select(location => LocationToView(location, longitudeOffset));
|
||||
|
||||
if (points.Any())
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,78 +0,0 @@
|
|||
using System;
|
||||
#if WPF
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
#elif AVALONIA
|
||||
using Avalonia;
|
||||
#endif
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
/// <summary>
|
||||
/// Spherical Azimuthal Equidistant Projection - AUTO2:97003.
|
||||
/// See "Map Projections - A Working Manual" (https://pubs.usgs.gov/publication/pp1395), p.195-197.
|
||||
/// </summary>
|
||||
public class AzimuthalEquidistantProjection : AzimuthalProjection
|
||||
{
|
||||
public const string DefaultCrsId = "AUTO2:97003"; // GeoServer non-standard CRS identifier
|
||||
|
||||
public AzimuthalEquidistantProjection() // parameterless constructor for XAML
|
||||
: this(DefaultCrsId)
|
||||
{
|
||||
}
|
||||
|
||||
public AzimuthalEquidistantProjection(string crsId)
|
||||
{
|
||||
CrsId = crsId;
|
||||
}
|
||||
|
||||
public override Matrix RelativeTransform(double latitude, double longitude)
|
||||
{
|
||||
var p = GetProjectedPoint(latitude, longitude);
|
||||
var scaleX = 1d;
|
||||
var scaleY = 1d;
|
||||
|
||||
if (p.CosC == -1d)
|
||||
{
|
||||
scaleY = double.PositiveInfinity;
|
||||
}
|
||||
else if (p.CosC != 1d)
|
||||
{
|
||||
var c = Math.Acos(p.CosC);
|
||||
var k = c / Math.Sin(c); // p.195 (25-2)
|
||||
|
||||
(scaleX, scaleY) = p.RelativeScale(1d, k);
|
||||
}
|
||||
|
||||
return RelativeTransform(latitude, longitude, scaleX, scaleY);
|
||||
}
|
||||
|
||||
public override Point? LocationToMap(double latitude, double longitude)
|
||||
{
|
||||
var p = GetProjectedPoint(latitude, longitude);
|
||||
|
||||
if (p.CosC == 1d) // p.195 "If cos c = 1, ... k' = 1, and x = y = 0."
|
||||
{
|
||||
return new Point();
|
||||
}
|
||||
|
||||
if (p.CosC == -1)
|
||||
{
|
||||
return null; // p.195 "If cos c = -1, the point ... is plotted as a circle of radius πR."
|
||||
}
|
||||
|
||||
var c = Math.Acos(p.CosC);
|
||||
var k = c / Math.Sin(c); // p.195 (25-2)
|
||||
|
||||
return new Point(EquatorialRadius * k * p.X, EquatorialRadius * k * p.Y); // p.195 (22-4/5)
|
||||
}
|
||||
|
||||
public override Location MapToLocation(double x, double y)
|
||||
{
|
||||
var rho = Math.Sqrt(x * x + y * y);
|
||||
var c = rho / EquatorialRadius; // p.196 (25-15)
|
||||
|
||||
return GetLocation(x, y, rho, Math.Sin(c));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,101 +0,0 @@
|
|||
using System;
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
public abstract class AzimuthalProjection : MapProjection
|
||||
{
|
||||
protected AzimuthalProjection()
|
||||
{
|
||||
EnableCenterUpdates();
|
||||
}
|
||||
|
||||
public readonly struct ProjectedPoint
|
||||
{
|
||||
public double X { get; }
|
||||
public double Y { get; }
|
||||
public double CosC { get; }
|
||||
|
||||
public ProjectedPoint(double centerLatitude, double centerLongitude, double latitude, double longitude)
|
||||
{
|
||||
var phi = latitude * Math.PI / 180d;
|
||||
var phi1 = centerLatitude * Math.PI / 180d;
|
||||
var dLambda = (longitude - centerLongitude) * Math.PI / 180d; // λ - λ0
|
||||
var cosPhi = Math.Cos(phi);
|
||||
var sinPhi = Math.Sin(phi);
|
||||
var cosPhi1 = Math.Cos(phi1);
|
||||
var sinPhi1 = Math.Sin(phi1);
|
||||
var cosLambda = Math.Cos(dLambda);
|
||||
var sinLambda = Math.Sin(dLambda);
|
||||
var cosC = sinPhi1 * sinPhi + cosPhi1 * cosPhi * cosLambda; // (5-3)
|
||||
|
||||
X = cosPhi * sinLambda;
|
||||
Y = cosPhi1 * sinPhi - sinPhi1 * cosPhi * cosLambda;
|
||||
CosC = Math.Min(Math.Max(cosC, -1d), 1d); // protect against rounding errors
|
||||
}
|
||||
|
||||
public (double, double) RelativeScale(double radialScale, double perpendicularScale)
|
||||
{
|
||||
var scaleX = radialScale;
|
||||
var scaleY = perpendicularScale;
|
||||
|
||||
if (scaleX != scaleY)
|
||||
{
|
||||
var s = Math.Sqrt(X * X + Y * Y);
|
||||
// sin and cos of azimuth from projection center, i.e. Atan2(Y/X)
|
||||
var cos = X / s;
|
||||
var sin = Y / s;
|
||||
var x1 = scaleX * cos;
|
||||
var y1 = scaleY * sin;
|
||||
var x2 = scaleX * sin;
|
||||
var y2 = scaleY * cos;
|
||||
scaleX = Math.Sqrt(x1 * x1 + y1 * y1);
|
||||
scaleY = Math.Sqrt(x2 * x2 + y2 * y2);
|
||||
}
|
||||
|
||||
return (scaleX, scaleY);
|
||||
}
|
||||
}
|
||||
|
||||
protected ProjectedPoint GetProjectedPoint(double latitude, double longitude)
|
||||
{
|
||||
return new ProjectedPoint(Center.Latitude, Center.Longitude, latitude, longitude);
|
||||
}
|
||||
|
||||
protected Location GetLocation(double x, double y, double rho, double sinC)
|
||||
{
|
||||
var cos2C = 1d - sinC * sinC;
|
||||
|
||||
if (cos2C < 0d)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var cosC = Math.Sqrt(cos2C);
|
||||
var phi1 = Center.Latitude * Math.PI / 180d;
|
||||
var cosPhi1 = Math.Cos(phi1);
|
||||
var sinPhi1 = Math.Sin(phi1);
|
||||
var phi = Math.Asin(cosC * sinPhi1 + y * sinC * cosPhi1 / rho); // (20-14)
|
||||
double u, v;
|
||||
|
||||
if (Center.Latitude == 90d) // (20-16)
|
||||
{
|
||||
u = x;
|
||||
v = -y;
|
||||
}
|
||||
else if (Center.Latitude == -90d) // (20-17)
|
||||
{
|
||||
u = x;
|
||||
v = y;
|
||||
}
|
||||
else // (20-15)
|
||||
{
|
||||
u = x * sinC;
|
||||
v = rho * cosPhi1 * cosC - y * sinPhi1 * sinC;
|
||||
}
|
||||
|
||||
return new Location(
|
||||
phi * 180d / Math.PI,
|
||||
Math.Atan2(u, v) * 180d / Math.PI + Center.Longitude);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -33,7 +33,7 @@ namespace MapControl
|
|||
return new Matrix(1d / Math.Cos(latitude * Math.PI / 180d), 0d, 0d, 1d, 0d, 0d);
|
||||
}
|
||||
|
||||
public override Point? LocationToMap(double latitude, double longitude)
|
||||
public override Point LocationToMap(double latitude, double longitude)
|
||||
{
|
||||
return new Point(MeterPerDegree * longitude, MeterPerDegree * latitude);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,61 +0,0 @@
|
|||
using System;
|
||||
#if WPF
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
#elif AVALONIA
|
||||
using Avalonia;
|
||||
#endif
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
/// <summary>
|
||||
/// Spherical Gnomonic Projection - AUTO2:97001.
|
||||
/// See "Map Projections - A Working Manual" (https://pubs.usgs.gov/publication/pp1395), p.165-167.
|
||||
/// </summary>
|
||||
public class GnomonicProjection : AzimuthalProjection
|
||||
{
|
||||
public const string DefaultCrsId = "AUTO2:97001"; // GeoServer non-standard CRS identifier
|
||||
|
||||
public GnomonicProjection() // parameterless constructor for XAML
|
||||
: this(DefaultCrsId)
|
||||
{
|
||||
}
|
||||
|
||||
public GnomonicProjection(string crsId)
|
||||
{
|
||||
CrsId = crsId;
|
||||
}
|
||||
|
||||
public override Matrix RelativeTransform(double latitude, double longitude)
|
||||
{
|
||||
var p = GetProjectedPoint(latitude, longitude);
|
||||
var k = 1d / p.CosC; // p.165 (22-3)
|
||||
var h = k * k; // p.165 (22-2)
|
||||
(var scaleX, var scaleY) = p.RelativeScale(h, k);
|
||||
|
||||
return RelativeTransform(latitude, longitude, scaleX, scaleY);
|
||||
}
|
||||
|
||||
public override Point? LocationToMap(double latitude, double longitude)
|
||||
{
|
||||
var p = GetProjectedPoint(latitude, longitude);
|
||||
|
||||
if (p.CosC <= 0d) // p.167 "If cos c is zero or negative, the point is to be rejected."
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var k = 1d / p.CosC; // p.165 (22-3)
|
||||
|
||||
return new Point(EquatorialRadius * k * p.X, EquatorialRadius * k * p.Y); // p.165 (22-4/5)
|
||||
}
|
||||
|
||||
public override Location MapToLocation(double x, double y)
|
||||
{
|
||||
var rho = Math.Sqrt(x * x + y * y);
|
||||
var c = Math.Atan(rho / EquatorialRadius); // p.167 (22-16)
|
||||
|
||||
return GetLocation(x, y, rho, Math.Sin(c));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -43,10 +43,6 @@ namespace MapControl
|
|||
DependencyPropertyHelper.Register<MapBase, MapProjection>(nameof(MapProjection), new WebMercatorProjection(),
|
||||
(map, oldValue, newValue) => map.MapProjectionPropertyChanged(newValue));
|
||||
|
||||
public static readonly DependencyProperty ProjectionCenterProperty =
|
||||
DependencyPropertyHelper.Register<MapBase, Location>(nameof(ProjectionCenter), null,
|
||||
(map, oldValue, newValue) => map.ProjectionCenterPropertyChanged(newValue));
|
||||
|
||||
private Location transformCenter;
|
||||
private Point viewCenter;
|
||||
private double centerLongitude;
|
||||
|
|
@ -86,17 +82,6 @@ namespace MapControl
|
|||
set => SetValue(MapProjectionProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a projection center e.g. for azimuthal projections.
|
||||
/// If ProjectionCenter is null, the value of the Center property is used as projection center.
|
||||
/// This behavior is overridden when the Center property of a MapProjection is set explicitly.
|
||||
/// </summary>
|
||||
public Location ProjectionCenter
|
||||
{
|
||||
get => (Location)GetValue(ProjectionCenterProperty);
|
||||
set => SetValue(ProjectionCenterProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the location of the center point of the map.
|
||||
/// </summary>
|
||||
|
|
@ -172,8 +157,8 @@ namespace MapControl
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the ViewTransform instance that is used to transform between projected
|
||||
/// map coordinates and view coordinates.
|
||||
/// Gets the ViewTransform instance that is used to transform between
|
||||
/// projected map coordinates and view coordinates.
|
||||
/// </summary>
|
||||
public ViewTransform ViewTransform { get; } = new ViewTransform();
|
||||
|
||||
|
|
@ -186,7 +171,6 @@ namespace MapControl
|
|||
var transform = MapProjection.RelativeTransform(latitude, longitude);
|
||||
transform.Scale(ViewTransform.Scale, ViewTransform.Scale);
|
||||
transform.Rotate(ViewTransform.Rotation);
|
||||
|
||||
return transform;
|
||||
}
|
||||
|
||||
|
|
@ -194,57 +178,27 @@ namespace MapControl
|
|||
/// Gets a transform Matrix from meters to view coordinates for scaling and rotating
|
||||
/// at the specified Location.
|
||||
/// </summary>
|
||||
public Matrix GetMapToViewTransform(Location location)
|
||||
{
|
||||
return GetMapToViewTransform(location.Latitude, location.Longitude);
|
||||
}
|
||||
public Matrix GetMapToViewTransform(Location location) => GetMapToViewTransform(location.Latitude, location.Longitude);
|
||||
|
||||
/// <summary>
|
||||
/// Transforms geographic coordinates to a Point in view coordinates.
|
||||
/// </summary>
|
||||
public Point? LocationToView(double latitude, double longitude)
|
||||
{
|
||||
var point = MapProjection.LocationToMap(latitude, longitude);
|
||||
|
||||
if (point.HasValue)
|
||||
{
|
||||
point = ViewTransform.MapToView(point.Value);
|
||||
}
|
||||
|
||||
return point;
|
||||
}
|
||||
public Point LocationToView(double latitude, double longitude) => ViewTransform.MapToView(MapProjection.LocationToMap(latitude, longitude));
|
||||
|
||||
/// <summary>
|
||||
/// Transforms a Location in geographic coordinates to a Point in view coordinates.
|
||||
/// </summary>
|
||||
public Point? LocationToView(Location location)
|
||||
{
|
||||
return LocationToView(location.Latitude, location.Longitude);
|
||||
}
|
||||
public Point LocationToView(Location location) => LocationToView(location.Latitude, location.Longitude);
|
||||
|
||||
/// <summary>
|
||||
/// Transforms a Point in view coordinates to a Location in geographic coordinates.
|
||||
/// </summary>
|
||||
public Location ViewToLocation(Point point)
|
||||
{
|
||||
return MapProjection.MapToLocation(ViewTransform.ViewToMap(point));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a MapRect in projected map coordinates that covers a rectangle in view coordinates.
|
||||
/// </summary>
|
||||
public MapRect ViewToMapRect(Rect rect)
|
||||
{
|
||||
return new MapRect(ViewTransform.ViewToMapBounds(rect), MapProjection.Center);
|
||||
}
|
||||
public Location ViewToLocation(Point point) => MapProjection.MapToLocation(ViewTransform.ViewToMap(point));
|
||||
|
||||
/// <summary>
|
||||
/// Gets a BoundingBox in geographic coordinates that covers a rectangle in view coordinates.
|
||||
/// </summary>
|
||||
public BoundingBox ViewToBoundingBox(Rect rect)
|
||||
{
|
||||
return MapProjection.MapToBoundingBox(ViewTransform.ViewToMapBounds(rect));
|
||||
}
|
||||
public BoundingBox ViewToBoundingBox(Rect rect) => MapProjection.MapToBoundingBox(ViewTransform.ViewToMapBounds(rect));
|
||||
|
||||
/// <summary>
|
||||
/// Sets a temporary center point in view coordinates for scaling and rotation transformations.
|
||||
|
|
@ -253,8 +207,8 @@ namespace MapControl
|
|||
/// </summary>
|
||||
public void SetTransformCenter(Point center)
|
||||
{
|
||||
viewCenter = center;
|
||||
transformCenter = ViewToLocation(center);
|
||||
viewCenter = transformCenter != null ? center : new Point(ActualWidth / 2d, ActualHeight / 2d);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -262,8 +216,8 @@ namespace MapControl
|
|||
/// </summary>
|
||||
public void ResetTransformCenter()
|
||||
{
|
||||
transformCenter = null;
|
||||
viewCenter = new Point(ActualWidth / 2d, ActualHeight / 2d);
|
||||
transformCenter = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -273,12 +227,7 @@ namespace MapControl
|
|||
{
|
||||
if (translation.X != 0d || translation.Y != 0d)
|
||||
{
|
||||
var center = ViewToLocation(new Point(viewCenter.X - translation.X, viewCenter.Y - translation.Y));
|
||||
|
||||
if (center != null)
|
||||
{
|
||||
Center = center;
|
||||
}
|
||||
Center = ViewToLocation(new Point(viewCenter.X - translation.X, viewCenter.Y - translation.Y));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -350,22 +299,11 @@ namespace MapControl
|
|||
/// </summary>
|
||||
public void ZoomToBounds(BoundingBox bounds)
|
||||
{
|
||||
(var mapRect, var _) = MapProjection.BoundingBoxToMap(bounds);
|
||||
|
||||
if (mapRect.HasValue)
|
||||
{
|
||||
var targetCenter = MapProjection.MapToLocation(
|
||||
mapRect.Value.X + mapRect.Value.Width / 2d,
|
||||
mapRect.Value.Y + mapRect.Value.Height / 2d);
|
||||
|
||||
if (targetCenter != null)
|
||||
{
|
||||
var scale = Math.Min(ActualWidth / mapRect.Value.Width, ActualHeight / mapRect.Value.Height);
|
||||
TargetZoomLevel = ScaleToZoomLevel(scale);
|
||||
TargetCenter = targetCenter;
|
||||
TargetHeading = 0d;
|
||||
}
|
||||
}
|
||||
(var rect, var _) = MapProjection.BoundingBoxToMap(bounds);
|
||||
var scale = Math.Min(ActualWidth / rect.Width, ActualHeight / rect.Height);
|
||||
TargetZoomLevel = ScaleToZoomLevel(scale);
|
||||
TargetCenter = new Location((bounds.South + bounds.North) / 2d, (bounds.West + bounds.East) / 2d);
|
||||
TargetHeading = 0d;
|
||||
}
|
||||
|
||||
internal bool InsideViewBounds(Point point)
|
||||
|
|
@ -441,107 +379,65 @@ namespace MapControl
|
|||
if (projection.IsNormalCylindrical)
|
||||
{
|
||||
var maxLocation = projection.MapToLocation(0d, 180d * MapProjection.Wgs84MeterPerDegree);
|
||||
|
||||
if (maxLocation != null && maxLocation.Latitude < 90d)
|
||||
{
|
||||
maxLatitude = maxLocation.Latitude;
|
||||
Center = CoerceCenterProperty(Center);
|
||||
}
|
||||
maxLatitude = maxLocation.Latitude;
|
||||
Center = CoerceCenterProperty(Center);
|
||||
}
|
||||
|
||||
ResetTransformCenter();
|
||||
UpdateTransform(false, true);
|
||||
}
|
||||
|
||||
private void ProjectionCenterPropertyChanged(Location center)
|
||||
{
|
||||
if (!internalPropertyChange)
|
||||
{
|
||||
if (center != null)
|
||||
{
|
||||
var longitude = Location.NormalizeLongitude(center.Longitude);
|
||||
|
||||
if (!center.LongitudeEquals(longitude))
|
||||
{
|
||||
SetValueInternal(ProjectionCenterProperty, new Location(center.Latitude, longitude));
|
||||
}
|
||||
}
|
||||
|
||||
ResetTransformCenter();
|
||||
UpdateTransform();
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateTransform(bool resetTransformCenter = false, bool projectionChanged = false)
|
||||
{
|
||||
var transformCenterChanged = false;
|
||||
var viewScale = ZoomLevelToScale(ZoomLevel);
|
||||
var projection = MapProjection;
|
||||
var mapCenter = MapProjection.LocationToMap(transformCenter ?? Center);
|
||||
|
||||
projection.SetCenter(ProjectionCenter ?? Center);
|
||||
ViewTransform.SetTransform(mapCenter, viewCenter, viewScale, -Heading);
|
||||
|
||||
var mapCenter = projection.LocationToMap(transformCenter ?? Center);
|
||||
|
||||
if (mapCenter.HasValue)
|
||||
if (transformCenter != null)
|
||||
{
|
||||
ViewTransform.SetTransform(mapCenter.Value, viewCenter, viewScale, -Heading);
|
||||
var center = ViewToLocation(new Point(ActualWidth / 2d, ActualHeight / 2d));
|
||||
var latitude = center.Latitude;
|
||||
var longitude = Location.NormalizeLongitude(center.Longitude);
|
||||
|
||||
if (transformCenter != null)
|
||||
if (latitude < -maxLatitude || latitude > maxLatitude)
|
||||
{
|
||||
var center = ViewToLocation(new Point(ActualWidth / 2d, ActualHeight / 2d));
|
||||
|
||||
if (center != null)
|
||||
{
|
||||
var latitude = center.Latitude;
|
||||
var longitude = Location.NormalizeLongitude(center.Longitude);
|
||||
|
||||
if (latitude < -maxLatitude || latitude > maxLatitude)
|
||||
{
|
||||
latitude = Math.Min(Math.Max(latitude, -maxLatitude), maxLatitude);
|
||||
resetTransformCenter = true;
|
||||
}
|
||||
|
||||
if (!center.Equals(latitude, longitude))
|
||||
{
|
||||
center = new Location(latitude, longitude);
|
||||
}
|
||||
|
||||
SetValueInternal(CenterProperty, center);
|
||||
|
||||
if (centerAnimation == null)
|
||||
{
|
||||
SetValueInternal(TargetCenterProperty, center);
|
||||
}
|
||||
|
||||
if (resetTransformCenter)
|
||||
{
|
||||
// Check if transform center has moved across 180° longitude.
|
||||
//
|
||||
transformCenterChanged = Math.Abs(center.Longitude - transformCenter.Longitude) > 180d;
|
||||
|
||||
ResetTransformCenter();
|
||||
|
||||
projection.SetCenter(ProjectionCenter ?? Center);
|
||||
|
||||
mapCenter = projection.LocationToMap(center);
|
||||
|
||||
if (mapCenter.HasValue)
|
||||
{
|
||||
ViewTransform.SetTransform(mapCenter.Value, viewCenter, viewScale, -Heading);
|
||||
}
|
||||
}
|
||||
}
|
||||
latitude = Math.Min(Math.Max(latitude, -maxLatitude), maxLatitude);
|
||||
resetTransformCenter = true;
|
||||
}
|
||||
|
||||
ViewScale = ViewTransform.Scale;
|
||||
if (!center.Equals(latitude, longitude))
|
||||
{
|
||||
center = new Location(latitude, longitude);
|
||||
}
|
||||
|
||||
// Check if view center has moved across 180° longitude.
|
||||
//
|
||||
transformCenterChanged = transformCenterChanged || Math.Abs(Center.Longitude - centerLongitude) > 180d;
|
||||
centerLongitude = Center.Longitude;
|
||||
SetValueInternal(CenterProperty, center);
|
||||
|
||||
OnViewportChanged(new ViewportChangedEventArgs(projectionChanged, transformCenterChanged));
|
||||
if (centerAnimation == null)
|
||||
{
|
||||
SetValueInternal(TargetCenterProperty, center);
|
||||
}
|
||||
|
||||
if (resetTransformCenter)
|
||||
{
|
||||
// Check if transform center has moved across 180° longitude.
|
||||
//
|
||||
transformCenterChanged = Math.Abs(center.Longitude - transformCenter.Longitude) > 180d;
|
||||
ResetTransformCenter();
|
||||
mapCenter = MapProjection.LocationToMap(center);
|
||||
ViewTransform.SetTransform(mapCenter, viewCenter, viewScale, -Heading);
|
||||
}
|
||||
}
|
||||
|
||||
ViewScale = ViewTransform.Scale;
|
||||
|
||||
// Check if view center has moved across 180° longitude.
|
||||
//
|
||||
transformCenterChanged = transformCenterChanged || Math.Abs(Center.Longitude - centerLongitude) > 180d;
|
||||
centerLongitude = Center.Longitude;
|
||||
|
||||
OnViewportChanged(new ViewportChangedEventArgs(projectionChanged, transformCenterChanged));
|
||||
}
|
||||
|
||||
protected override void OnViewportChanged(ViewportChangedEventArgs e)
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ namespace MapControl
|
|||
return (bool)element.GetValue(OnBorderProperty);
|
||||
}
|
||||
|
||||
protected override void SetViewPosition(FrameworkElement element, ref Point? position)
|
||||
protected override Point SetViewPosition(FrameworkElement element, Point position)
|
||||
{
|
||||
var onBorder = false;
|
||||
var w = ParentMap.ActualWidth;
|
||||
|
|
@ -45,12 +45,11 @@ namespace MapControl
|
|||
var maxX = w - BorderWidth / 2d;
|
||||
var maxY = h - BorderWidth / 2d;
|
||||
|
||||
if (position.HasValue &&
|
||||
(position.Value.X < minX || position.Value.X > maxX ||
|
||||
position.Value.Y < minY || position.Value.Y > maxY))
|
||||
if (position.X < minX || position.X > maxX ||
|
||||
position.Y < minY || position.Y > maxY)
|
||||
{
|
||||
var dx = position.Value.X - w / 2d;
|
||||
var dy = position.Value.Y - h / 2d;
|
||||
var dx = position.X - w / 2d;
|
||||
var dy = position.Y - h / 2d;
|
||||
var cx = (maxX - minX) / 2d;
|
||||
var cy = (maxY - minY) / 2d;
|
||||
double x, y;
|
||||
|
|
@ -86,7 +85,7 @@ namespace MapControl
|
|||
|
||||
element.SetValue(OnBorderProperty, onBorder);
|
||||
|
||||
base.SetViewPosition(element, ref position);
|
||||
return base.SetViewPosition(element, position);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -127,10 +127,7 @@ namespace MapControl
|
|||
//
|
||||
var pos = ParentMap.LocationToView(latitude, longitude + 10d / PixelPerDegree);
|
||||
|
||||
if (pos.HasValue)
|
||||
{
|
||||
rotation = Math.Atan2(pos.Value.Y - position.Y, pos.Value.X - position.X) * 180d / Math.PI;
|
||||
}
|
||||
rotation = Math.Atan2(pos.Y - position.Y, pos.X - position.X) * 180d / Math.PI;
|
||||
}
|
||||
|
||||
if (rotation.HasValue)
|
||||
|
|
@ -173,31 +170,19 @@ namespace MapControl
|
|||
{
|
||||
var p1 = ParentMap.LocationToView(lat, boundingBox.West);
|
||||
var p2 = ParentMap.LocationToView(lat, boundingBox.East);
|
||||
|
||||
if (p1.HasValue && p2.HasValue)
|
||||
{
|
||||
figures.Add(CreateLineFigure(p1.Value, p2.Value));
|
||||
}
|
||||
figures.Add(CreateLineFigure(p1, p2));
|
||||
}
|
||||
|
||||
for (var lon = lonLabelStart; lon <= boundingBox.East; lon += lineDistance)
|
||||
{
|
||||
var p1 = ParentMap.LocationToView(boundingBox.South, lon);
|
||||
var p2 = ParentMap.LocationToView(boundingBox.North, lon);
|
||||
|
||||
if (p1.HasValue && p2.HasValue)
|
||||
{
|
||||
figures.Add(CreateLineFigure(p1.Value, p2.Value));
|
||||
}
|
||||
figures.Add(CreateLineFigure(p1, p2));
|
||||
|
||||
for (var lat = latLabelStart; lat <= boundingBox.North; lat += lineDistance)
|
||||
{
|
||||
var position = ParentMap.LocationToView(lat, lon);
|
||||
|
||||
if (position.HasValue)
|
||||
{
|
||||
AddLabel(labels, lat, lon, position.Value, ParentMap.ViewTransform.Rotation);
|
||||
}
|
||||
AddLabel(labels, lat, lon, position, ParentMap.ViewTransform.Rotation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -210,7 +195,6 @@ namespace MapControl
|
|||
var interpolationCount = Math.Max(1, (int)Math.Ceiling(lineDistance / LineInterpolationResolution));
|
||||
var interpolationDistance = lineDistance / interpolationCount;
|
||||
var latPoints = latSegments * interpolationCount;
|
||||
|
||||
var centerLon = Math.Round(ParentMap.Center.Longitude / lineDistance) * lineDistance;
|
||||
var minLon = centerLon - lineDistance;
|
||||
var maxLon = centerLon + lineDistance;
|
||||
|
|
@ -239,11 +223,8 @@ namespace MapControl
|
|||
var points = new List<Point>();
|
||||
var position = ParentMap.LocationToView(lat, lon);
|
||||
|
||||
if (position.HasValue)
|
||||
{
|
||||
points.Add(position.Value);
|
||||
AddLabel(labels, lat, lon, position.Value);
|
||||
}
|
||||
points.Add(position);
|
||||
AddLabel(labels, lat, lon, position);
|
||||
|
||||
for (int j = 0; j < lonSegments; j++)
|
||||
{
|
||||
|
|
@ -251,17 +232,10 @@ namespace MapControl
|
|||
{
|
||||
lon = minLon + j * lineDistance + k * interpolationDistance;
|
||||
position = ParentMap.LocationToView(lat, lon);
|
||||
|
||||
if (position.HasValue)
|
||||
{
|
||||
points.Add(position.Value);
|
||||
}
|
||||
points.Add(position);
|
||||
}
|
||||
|
||||
if (position.HasValue)
|
||||
{
|
||||
AddLabel(labels, lat, lon, position.Value);
|
||||
}
|
||||
AddLabel(labels, lat, lon, position);
|
||||
}
|
||||
|
||||
if (points.Count >= 2)
|
||||
|
|
@ -281,14 +255,11 @@ namespace MapControl
|
|||
{
|
||||
var p = ParentMap.LocationToView(startLatitude + i * deltaLatitude, longitude);
|
||||
|
||||
if (p.HasValue)
|
||||
{
|
||||
visible = visible ||
|
||||
p.Value.X >= 0d && p.Value.X <= ParentMap.ActualWidth &&
|
||||
p.Value.Y >= 0d && p.Value.Y <= ParentMap.ActualHeight;
|
||||
visible = visible ||
|
||||
p.X >= 0d && p.X <= ParentMap.ActualWidth &&
|
||||
p.Y >= 0d && p.Y <= ParentMap.ActualHeight;
|
||||
|
||||
points.Add(p.Value);
|
||||
}
|
||||
points.Add(p);
|
||||
}
|
||||
|
||||
if (points.Count >= 2)
|
||||
|
|
|
|||
|
|
@ -180,7 +180,7 @@ namespace MapControl
|
|||
updateTimer.Stop();
|
||||
|
||||
ImageSource image = null;
|
||||
MapRect mapRect = null;
|
||||
Rect? mapRect = null;
|
||||
|
||||
if (ParentMap != null &&
|
||||
(SupportedCrsIds == null || SupportedCrsIds.Contains(ParentMap.MapProjection.CrsId)))
|
||||
|
|
@ -193,8 +193,8 @@ namespace MapControl
|
|||
var x = (ParentMap.ActualWidth - width) / 2d;
|
||||
var y = (ParentMap.ActualHeight - height) / 2d;
|
||||
|
||||
mapRect = ParentMap.ViewToMapRect(new Rect(x, y, width, height));
|
||||
image = await GetImageAsync(mapRect.Rect, loadingProgress);
|
||||
mapRect = ParentMap.ViewTransform.ViewToMapBounds(new Rect(x, y, width, height));
|
||||
image = await GetImageAsync(mapRect.Value, loadingProgress);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -216,7 +216,7 @@ namespace MapControl
|
|||
}
|
||||
}
|
||||
|
||||
private void SwapImages(ImageSource image, MapRect mapRect)
|
||||
private void SwapImages(ImageSource image, Rect? mapRect)
|
||||
{
|
||||
if (Children.Count >= 2)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -131,15 +131,15 @@ namespace MapControl
|
|||
/// <summary>
|
||||
/// Gets the MapRect of an element.
|
||||
/// </summary>
|
||||
public static MapRect GetMapRect(FrameworkElement element)
|
||||
public static Rect? GetMapRect(FrameworkElement element)
|
||||
{
|
||||
return (MapRect)element.GetValue(MapRectProperty);
|
||||
return (Rect?)element.GetValue(MapRectProperty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the MapRect of an element.
|
||||
/// </summary>
|
||||
public static void SetMapRect(FrameworkElement element, MapRect value)
|
||||
public static void SetMapRect(FrameworkElement element, Rect? value)
|
||||
{
|
||||
element.SetValue(MapRectProperty, value);
|
||||
}
|
||||
|
|
@ -157,9 +157,11 @@ namespace MapControl
|
|||
/// ArrangeOverride and may be overridden to modify the actual view position value.
|
||||
/// An overridden method should call this method to set the attached property.
|
||||
/// </summary>
|
||||
protected virtual void SetViewPosition(FrameworkElement element, ref Point? position)
|
||||
protected virtual Point SetViewPosition(FrameworkElement element, Point position)
|
||||
{
|
||||
element.SetValue(ViewPositionProperty, position);
|
||||
|
||||
return position;
|
||||
}
|
||||
|
||||
protected virtual void SetParentMap(MapBase map)
|
||||
|
|
@ -214,19 +216,17 @@ namespace MapControl
|
|||
return finalSize;
|
||||
}
|
||||
|
||||
private Point? GetViewPosition(Location location)
|
||||
private Point GetViewPosition(Location location)
|
||||
{
|
||||
var position = parentMap.LocationToView(location);
|
||||
|
||||
if (parentMap.MapProjection.IsNormalCylindrical &&
|
||||
position.HasValue && !parentMap.InsideViewBounds(position.Value))
|
||||
if (parentMap.MapProjection.IsNormalCylindrical && !parentMap.InsideViewBounds(position))
|
||||
{
|
||||
var nearestPosition = parentMap.LocationToView(
|
||||
location.Latitude, parentMap.NearestLongitude(location.Longitude));
|
||||
var longitude = parentMap.NearestLongitude(location.Longitude);
|
||||
|
||||
if (nearestPosition.HasValue)
|
||||
if (longitude != location.Longitude)
|
||||
{
|
||||
position = nearestPosition;
|
||||
position = parentMap.LocationToView(location.Latitude, longitude);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -238,20 +238,14 @@ namespace MapControl
|
|||
var center = new Point(mapRect.X + mapRect.Width / 2d, mapRect.Y + mapRect.Height / 2d);
|
||||
var position = parentMap.ViewTransform.MapToView(center);
|
||||
|
||||
if (parentMap.MapProjection.IsNormalCylindrical &&
|
||||
!parentMap.InsideViewBounds(position))
|
||||
if (parentMap.MapProjection.IsNormalCylindrical && !parentMap.InsideViewBounds(position))
|
||||
{
|
||||
var location = parentMap.MapProjection.MapToLocation(center);
|
||||
var longitude = parentMap.NearestLongitude(location.Longitude);
|
||||
|
||||
if (location != null)
|
||||
if (longitude != location.Longitude)
|
||||
{
|
||||
var nearestPosition = parentMap.LocationToView(
|
||||
location.Latitude, parentMap.NearestLongitude(location.Longitude));
|
||||
|
||||
if (nearestPosition.HasValue)
|
||||
{
|
||||
position = nearestPosition.Value;
|
||||
}
|
||||
position = parentMap.LocationToView(location.Latitude, longitude);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -269,19 +263,14 @@ namespace MapControl
|
|||
|
||||
if (location != null)
|
||||
{
|
||||
var position = GetViewPosition(location);
|
||||
|
||||
SetViewPosition(element, ref position);
|
||||
var position = SetViewPosition(element, GetViewPosition(location));
|
||||
|
||||
if (GetAutoCollapse(element))
|
||||
{
|
||||
element.SetVisible(position.HasValue && parentMap.InsideViewBounds(position.Value));
|
||||
element.SetVisible(parentMap.InsideViewBounds(position));
|
||||
}
|
||||
|
||||
if (position.HasValue)
|
||||
{
|
||||
ArrangeElement(element, position.Value);
|
||||
}
|
||||
ArrangeElement(element, position);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -289,11 +278,9 @@ namespace MapControl
|
|||
|
||||
var mapRect = GetMapRect(element);
|
||||
|
||||
if (mapRect != null)
|
||||
if (mapRect.HasValue)
|
||||
{
|
||||
mapRect.Update(parentMap.MapProjection);
|
||||
|
||||
ArrangeElement(element, mapRect.Rect, null);
|
||||
ArrangeElement(element, mapRect.Value, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -303,10 +290,7 @@ namespace MapControl
|
|||
{
|
||||
(var rect, var transform) = parentMap.MapProjection.BoundingBoxToMap(boundingBox);
|
||||
|
||||
if (rect.HasValue)
|
||||
{
|
||||
ArrangeElement(element, rect.Value, transform);
|
||||
}
|
||||
ArrangeElement(element, rect, transform);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ namespace MapControl
|
|||
{
|
||||
var position = ParentMap.LocationToView(location);
|
||||
|
||||
if (position.HasValue && !ParentMap.InsideViewBounds(position.Value))
|
||||
if (!ParentMap.InsideViewBounds(position))
|
||||
{
|
||||
longitudeOffset = ParentMap.NearestLongitude(location.Longitude) - location.Longitude;
|
||||
}
|
||||
|
|
@ -89,35 +89,25 @@ namespace MapControl
|
|||
return longitudeOffset;
|
||||
}
|
||||
|
||||
protected Point? LocationToMap(Location location, double longitudeOffset)
|
||||
protected Point LocationToMap(Location location, double longitudeOffset)
|
||||
{
|
||||
var point = ParentMap.MapProjection.LocationToMap(location.Latitude, location.Longitude + longitudeOffset);
|
||||
|
||||
if (point.HasValue)
|
||||
if (point.Y == double.PositiveInfinity)
|
||||
{
|
||||
if (point.Value.Y == double.PositiveInfinity)
|
||||
{
|
||||
point = new Point(point.Value.X, 1e9);
|
||||
}
|
||||
else if (point.Value.Y == double.NegativeInfinity)
|
||||
{
|
||||
point = new Point(point.Value.X, -1e9);
|
||||
}
|
||||
point = new Point(point.X, 1e9);
|
||||
}
|
||||
else if (point.Y == double.NegativeInfinity)
|
||||
{
|
||||
point = new Point(point.X, -1e9);
|
||||
}
|
||||
|
||||
return point;
|
||||
}
|
||||
|
||||
protected Point? LocationToView(Location location, double longitudeOffset)
|
||||
protected Point LocationToView(Location location, double longitudeOffset)
|
||||
{
|
||||
var point = LocationToMap(location, longitudeOffset);
|
||||
|
||||
if (point.HasValue)
|
||||
{
|
||||
point = ParentMap.ViewTransform.MapToView(point.Value);
|
||||
}
|
||||
|
||||
return point;
|
||||
return ParentMap.ViewTransform.MapToView(LocationToMap(location, longitudeOffset));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,54 +55,6 @@ namespace MapControl
|
|||
|
||||
public double MeterPerDegree => EquatorialRadius * Math.PI / 180d;
|
||||
|
||||
private Location center;
|
||||
private bool updateCenter;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets an optional projection center. If the property is set to a non-null value,
|
||||
/// it overrides the projection center set by MapBase.Center or MapBase.ProjectionCenter.
|
||||
/// </summary>
|
||||
public Location Center
|
||||
{
|
||||
get => center;
|
||||
set
|
||||
{
|
||||
updateCenter = true;
|
||||
|
||||
if (value != null)
|
||||
{
|
||||
var longitude = Location.NormalizeLongitude(value.Longitude);
|
||||
|
||||
SetCenter(value.LongitudeEquals(longitude) ? value : new Location(value.Latitude, longitude));
|
||||
|
||||
updateCenter = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void EnableCenterUpdates()
|
||||
{
|
||||
updateCenter = true;
|
||||
SetCenter(new Location());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called by MapBase.UpdateTransform().
|
||||
/// </summary>
|
||||
internal void SetCenter(Location value)
|
||||
{
|
||||
if (updateCenter)
|
||||
{
|
||||
if (center == null || !center.Equals(value))
|
||||
{
|
||||
center = value;
|
||||
CenterChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void CenterChanged() { }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the relative transform at the specified geographic coordinates.
|
||||
/// The returned Matrix represents the local distortion of the map projection.
|
||||
|
|
@ -111,13 +63,11 @@ namespace MapControl
|
|||
|
||||
/// <summary>
|
||||
/// Transforms geographic coordinates to a Point in projected map coordinates.
|
||||
/// Returns null when the location can not be transformed.
|
||||
/// </summary>
|
||||
public abstract Point? LocationToMap(double latitude, double longitude);
|
||||
public abstract Point LocationToMap(double latitude, double longitude);
|
||||
|
||||
/// <summary>
|
||||
/// Transforms projected map coordinates to a Location in geographic coordinates.
|
||||
/// Returns null when the coordinates can not be transformed.
|
||||
/// </summary>
|
||||
public abstract Location MapToLocation(double x, double y);
|
||||
|
||||
|
|
@ -128,51 +78,40 @@ namespace MapControl
|
|||
|
||||
/// <summary>
|
||||
/// Transforms a Location in geographic coordinates to a Point in projected map coordinates.
|
||||
/// Returns null when the Location can not be transformed.
|
||||
/// </summary>
|
||||
public Point? LocationToMap(Location location) => LocationToMap(location.Latitude, location.Longitude);
|
||||
public Point LocationToMap(Location location) => LocationToMap(location.Latitude, location.Longitude);
|
||||
|
||||
/// <summary>
|
||||
/// Transforms a Point in projected map coordinates to a Location in geographic coordinates.
|
||||
/// Returns null when the Point can not be transformed.
|
||||
/// </summary>
|
||||
public Location MapToLocation(Point point) => MapToLocation(point.X, point.Y);
|
||||
|
||||
/// <summary>
|
||||
/// Transforms a BoundingBox in geographic coordinates to a Rect in projected map coordinates with
|
||||
/// an optional transform Matrix. Returns (null, null) when the BoundingBox can not be transformed.
|
||||
/// Transforms a BoundingBox in geographic coordinates to a Rect in projected map coordinates
|
||||
/// with an optional transform Matrix.
|
||||
/// </summary>
|
||||
public (Rect?, Matrix?) BoundingBoxToMap(BoundingBox boundingBox)
|
||||
public (Rect, Matrix?) BoundingBoxToMap(BoundingBox boundingBox)
|
||||
{
|
||||
Rect? rect = null;
|
||||
Rect rect;
|
||||
Matrix? transform = null;
|
||||
var sw = LocationToMap(boundingBox.South, boundingBox.West);
|
||||
var se = LocationToMap(boundingBox.South, boundingBox.East);
|
||||
var nw = LocationToMap(boundingBox.North, boundingBox.West);
|
||||
var ne = LocationToMap(boundingBox.North, boundingBox.East);
|
||||
|
||||
if (sw.HasValue && se.HasValue && nw.HasValue && ne.HasValue)
|
||||
if (IsNormalCylindrical)
|
||||
{
|
||||
var south = new Point((sw.Value.X + se.Value.X) / 2d, (sw.Value.Y + se.Value.Y) / 2d); // south midpoint
|
||||
var north = new Point((nw.Value.X + ne.Value.X) / 2d, (nw.Value.Y + ne.Value.Y) / 2d); // north midpoint
|
||||
var west = new Point((nw.Value.X + sw.Value.X) / 2d, (nw.Value.Y + sw.Value.Y) / 2d); // west midpoint
|
||||
var east = new Point((ne.Value.X + se.Value.X) / 2d, (ne.Value.Y + se.Value.Y) / 2d); // east midpoint
|
||||
var center = new Point((west.X + east.X) / 2d, (west.Y + east.Y) / 2d); // midpoint of segment west-east
|
||||
var dx1 = east.X - west.X;
|
||||
var dy1 = east.Y - west.Y;
|
||||
var dx2 = north.X - south.X;
|
||||
var dy2 = north.Y - south.Y;
|
||||
var width = Math.Sqrt(dx1 * dx1 + dy1 * dy1); // distance west-east
|
||||
var height = Math.Sqrt(dx2 * dx2 + dy2 * dy2); // distance south-north
|
||||
var southWest = LocationToMap(boundingBox.South, boundingBox.West);
|
||||
var northEast = LocationToMap(boundingBox.North, boundingBox.East);
|
||||
|
||||
rect = new Rect(southWest.X, southWest.Y, northEast.X - southWest.X, northEast.Y - southWest.Y);
|
||||
}
|
||||
else
|
||||
{
|
||||
var latitude = (boundingBox.South + boundingBox.North) / 2d;
|
||||
var longitude = (boundingBox.West + boundingBox.East) / 2d;
|
||||
var center = LocationToMap(latitude, longitude);
|
||||
var width = MeterPerDegree * (boundingBox.East - boundingBox.West) * Math.Cos(latitude * Math.PI / 180d);
|
||||
var height = MeterPerDegree * (boundingBox.North - boundingBox.South);
|
||||
|
||||
rect = new Rect(center.X - width / 2d, center.Y - height / 2d, width, height);
|
||||
|
||||
if (dy1 != 0d || dx2 != 0d)
|
||||
{
|
||||
// Skew matrix with skewX = Atan(-dx2 / dy2) and skewY = Atan(-dy1 / dx1).
|
||||
//
|
||||
transform = new Matrix(1d, -dy1 / dx1, -dx2 / dy2, 1d, 0d, 0d);
|
||||
}
|
||||
transform = RelativeTransform(latitude, longitude);
|
||||
}
|
||||
|
||||
return (rect, transform);
|
||||
|
|
@ -180,44 +119,18 @@ namespace MapControl
|
|||
|
||||
/// <summary>
|
||||
/// Transforms a Rect in projected map coordinates to a BoundingBox in geographic coordinates.
|
||||
/// Returns null when the Rect can not be transformed.
|
||||
/// </summary>
|
||||
public BoundingBox MapToBoundingBox(Rect rect)
|
||||
{
|
||||
var sw = MapToLocation(rect.X, rect.Y);
|
||||
var ne = MapToLocation(rect.X + rect.Width, rect.Y + rect.Height);
|
||||
var southWest = MapToLocation(rect.X, rect.Y);
|
||||
var northEast = MapToLocation(rect.X + rect.Width, rect.Y + rect.Height);
|
||||
|
||||
return sw != null && ne != null ? new BoundingBox(sw.Latitude, sw.Longitude, ne.Latitude, ne.Longitude) : null;
|
||||
return new BoundingBox(southWest.Latitude, southWest.Longitude, northEast.Latitude, northEast.Longitude);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return CrsId;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used by azimuthal projections, where local skewing can not be directly calculated.
|
||||
/// </summary>
|
||||
protected Matrix RelativeTransform(double latitude, double longitude, double scaleX, double scaleY)
|
||||
{
|
||||
var north = LocationToMap(latitude - 1e-3, longitude);
|
||||
var south = LocationToMap(latitude + 1e-3, longitude);
|
||||
var west = LocationToMap(latitude, longitude - 1e-3);
|
||||
var east = LocationToMap(latitude, longitude + 1e-3);
|
||||
var tanSkewX = 0d;
|
||||
var tanSkewY = 0d;
|
||||
|
||||
if (north.HasValue && south.HasValue && west.HasValue && east.HasValue)
|
||||
{
|
||||
var dx1 = east.Value.X - west.Value.X;
|
||||
var dy1 = east.Value.Y - west.Value.Y;
|
||||
var dx2 = north.Value.X - south.Value.X;
|
||||
var dy2 = north.Value.Y - south.Value.Y;
|
||||
tanSkewX = -dx2 / dy2;
|
||||
tanSkewY = -dy1 / dx1;
|
||||
}
|
||||
|
||||
return new Matrix(scaleX, scaleX * tanSkewY, scaleY * tanSkewX, scaleY, 0d, 0d);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,12 +13,6 @@ namespace MapControl
|
|||
EquirectangularProjection.DefaultCrsId or "CRS:84" => new EquirectangularProjection(crsId),
|
||||
Wgs84UpsNorthProjection.DefaultCrsId => new Wgs84UpsNorthProjection(),
|
||||
Wgs84UpsSouthProjection.DefaultCrsId => new Wgs84UpsSouthProjection(),
|
||||
Wgs84AutoUtmProjection.DefaultCrsId => new Wgs84AutoUtmProjection(),
|
||||
Wgs84AutoTmProjection.DefaultCrsId => new Wgs84AutoTmProjection(),
|
||||
OrthographicProjection.DefaultCrsId => new OrthographicProjection(),
|
||||
GnomonicProjection.DefaultCrsId => new GnomonicProjection(),
|
||||
StereographicProjection.DefaultCrsId => new StereographicProjection(),
|
||||
AzimuthalEquidistantProjection.DefaultCrsId => new AzimuthalEquidistantProjection(),
|
||||
_ => GetProjectionFromEpsgCode(crsId),
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,27 +0,0 @@
|
|||
#if WPF
|
||||
using System.Windows;
|
||||
#elif AVALONIA
|
||||
using Avalonia;
|
||||
#endif
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
public class MapRect(Rect rect, Location origin)
|
||||
{
|
||||
public Rect Rect { get; private set; } = rect;
|
||||
public Location Origin { get; private set; } = origin;
|
||||
|
||||
public void Update(MapProjection projection)
|
||||
{
|
||||
Point? origin;
|
||||
|
||||
if (Origin != null && projection.Center != null &&
|
||||
!Origin.Equals(projection.Center) &&
|
||||
(origin = projection.LocationToMap(Origin)).HasValue)
|
||||
{
|
||||
Rect = new Rect(Rect.X + origin.Value.X, Rect.Y + origin.Value.Y, Rect.Width, Rect.Height);
|
||||
Origin = projection.Center;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -92,35 +92,31 @@ namespace MapControl
|
|||
var y0 = ParentMap.ActualHeight / 2d;
|
||||
var p1 = ParentMap.ViewToLocation(new Point(x0 - 50d, y0));
|
||||
var p2 = ParentMap.ViewToLocation(new Point(x0 + 50d, y0));
|
||||
var scale = 100d / p1.GetDistance(p2);
|
||||
var length = MinWidth / scale;
|
||||
var magnitude = Math.Pow(10d, Math.Floor(Math.Log10(length)));
|
||||
|
||||
if (p1 != null && p2 != null)
|
||||
{
|
||||
var scale = 100d / p1.GetDistance(p2);
|
||||
var length = MinWidth / scale;
|
||||
var magnitude = Math.Pow(10d, Math.Floor(Math.Log10(length)));
|
||||
length = length / magnitude < 2d ? 2d * magnitude
|
||||
: length / magnitude < 5d ? 5d * magnitude
|
||||
: 10d * magnitude;
|
||||
|
||||
length = length / magnitude < 2d ? 2d * magnitude
|
||||
: length / magnitude < 5d ? 5d * magnitude
|
||||
: 10d * magnitude;
|
||||
size = new Size(
|
||||
length * scale + StrokeThickness + Padding.Left + Padding.Right,
|
||||
1.5 * label.FontSize + 2 * StrokeThickness + Padding.Top + Padding.Bottom);
|
||||
|
||||
size = new Size(
|
||||
length * scale + StrokeThickness + Padding.Left + Padding.Right,
|
||||
1.5 * label.FontSize + 2 * StrokeThickness + Padding.Top + Padding.Bottom);
|
||||
var x1 = Padding.Left + StrokeThickness / 2d;
|
||||
var x2 = size.Width - Padding.Right - StrokeThickness / 2d;
|
||||
var y1 = size.Height / 2d;
|
||||
var y2 = size.Height - Padding.Bottom - StrokeThickness / 2d;
|
||||
|
||||
var x1 = Padding.Left + StrokeThickness / 2d;
|
||||
var x2 = size.Width - Padding.Right - StrokeThickness / 2d;
|
||||
var y1 = size.Height / 2d;
|
||||
var y2 = size.Height - Padding.Bottom - StrokeThickness / 2d;
|
||||
line.Points = [new Point(x1, y1), new Point(x1, y2), new Point(x2, y2), new Point(x2, y1)];
|
||||
|
||||
line.Points = [new Point(x1, y1), new Point(x1, y2), new Point(x2, y2), new Point(x2, y1)];
|
||||
label.Text = length >= 1000d
|
||||
? string.Format(CultureInfo.InvariantCulture, "{0:F0} km", length / 1000d)
|
||||
: string.Format(CultureInfo.InvariantCulture, "{0:F0} m", length);
|
||||
|
||||
label.Text = length >= 1000d
|
||||
? string.Format(CultureInfo.InvariantCulture, "{0:F0} km", length / 1000d)
|
||||
: string.Format(CultureInfo.InvariantCulture, "{0:F0} m", length);
|
||||
|
||||
line.Measure(size);
|
||||
label.Measure(size);
|
||||
}
|
||||
line.Measure(size);
|
||||
label.Measure(size);
|
||||
}
|
||||
|
||||
return size;
|
||||
|
|
|
|||
|
|
@ -1,57 +0,0 @@
|
|||
using System;
|
||||
#if WPF
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
#elif AVALONIA
|
||||
using Avalonia;
|
||||
#endif
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
/// <summary>
|
||||
/// Spherical Orthographic Projection - AUTO2:42003.
|
||||
/// See "Map Projections - A Working Manual" (https://pubs.usgs.gov/publication/pp1395), p.148-150.
|
||||
/// </summary>
|
||||
public class OrthographicProjection : AzimuthalProjection
|
||||
{
|
||||
public const string DefaultCrsId = "AUTO2:42003";
|
||||
|
||||
public OrthographicProjection() // parameterless constructor for XAML
|
||||
: this(DefaultCrsId)
|
||||
{
|
||||
}
|
||||
|
||||
public OrthographicProjection(string crsId)
|
||||
{
|
||||
CrsId = crsId;
|
||||
}
|
||||
|
||||
public override Matrix RelativeTransform(double latitude, double longitude)
|
||||
{
|
||||
var p = GetProjectedPoint(latitude, longitude);
|
||||
(var scaleX, var scaleY) = p.RelativeScale(p.CosC, 1d); // p.149 (20-5), k == 1
|
||||
|
||||
return RelativeTransform(latitude, longitude, scaleX, scaleY);
|
||||
}
|
||||
|
||||
public override Point? LocationToMap(double latitude, double longitude)
|
||||
{
|
||||
var p = GetProjectedPoint(latitude, longitude);
|
||||
|
||||
if (p.CosC < 0d) // p.149 "If cos c is negative, the point is not to be plotted."
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return new Point(EquatorialRadius * p.X, EquatorialRadius * p.Y); // p.149 (20-3/4)
|
||||
}
|
||||
|
||||
public override Location MapToLocation(double x, double y)
|
||||
{
|
||||
var rho = Math.Sqrt(x * x + y * y);
|
||||
var sinC = rho / EquatorialRadius; // p.150 (20-19)
|
||||
|
||||
return GetLocation(x, y, rho, sinC);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -21,7 +21,7 @@ namespace MapControl
|
|||
public double FalseNorthing { get; set; } = 2e6;
|
||||
public Hemisphere Hemisphere { get; set; }
|
||||
|
||||
public static double RelativeScale(Hemisphere hemisphere, double flattening, double scaleFactor, double latitude)
|
||||
public static Matrix RelativeScale(Hemisphere hemisphere, double flattening, double scaleFactor, double latitude, double longitude)
|
||||
{
|
||||
var sign = hemisphere == Hemisphere.North ? 1d : -1d;
|
||||
var phi = sign * latitude * Math.PI / 180d;
|
||||
|
|
@ -33,20 +33,21 @@ namespace MapControl
|
|||
/ Math.Pow((1d - eSinPhi) / (1d + eSinPhi), e / 2d); // p.161 (15-9)
|
||||
|
||||
// r == ρ/a
|
||||
var r = scaleFactor * 2d * t / Math.Sqrt(Math.Pow(1d + e, 1d + e) * Math.Pow(1d - e, 1d - e)); // p.161 (21-33)
|
||||
var r = 2d * scaleFactor * t / Math.Sqrt(Math.Pow(1d + e, 1d + e) * Math.Pow(1d - e, 1d - e)); // p.161 (21-33)
|
||||
var m = Math.Cos(phi) / Math.Sqrt(1d - eSinPhi * eSinPhi); // p.160 (14-15)
|
||||
var k = r / m; // p.161 (21-32)
|
||||
|
||||
return r / m; // p.161 (21-32)
|
||||
var transform = new Matrix(k, 0d, 0d, k, 0d, 0d);
|
||||
transform.Rotate(-sign * longitude);
|
||||
return transform;
|
||||
}
|
||||
|
||||
public override Matrix RelativeTransform(double latitude, double longitude)
|
||||
{
|
||||
var k = RelativeScale(Hemisphere, Flattening, ScaleFactor, latitude);
|
||||
|
||||
return RelativeTransform(latitude, longitude, k, k);
|
||||
return RelativeScale(Hemisphere, Flattening, ScaleFactor, latitude, longitude);
|
||||
}
|
||||
|
||||
public override Point? LocationToMap(double latitude, double longitude)
|
||||
public override Point LocationToMap(double latitude, double longitude)
|
||||
{
|
||||
var sign = Hemisphere == Hemisphere.North ? 1d : -1d;
|
||||
var phi = sign * latitude * Math.PI / 180d;
|
||||
|
|
|
|||
|
|
@ -1,53 +0,0 @@
|
|||
using System;
|
||||
#if WPF
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
#elif AVALONIA
|
||||
using Avalonia;
|
||||
#endif
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
/// <summary>
|
||||
/// Spherical Stereographic Projection - AUTO2:97002.
|
||||
/// See "Map Projections - A Working Manual" (https://pubs.usgs.gov/publication/pp1395), p.157-160.
|
||||
/// </summary>
|
||||
public class StereographicProjection : AzimuthalProjection
|
||||
{
|
||||
public const string DefaultCrsId = "AUTO2:97002"; // GeoServer non-standard CRS identifier
|
||||
|
||||
public StereographicProjection() // parameterless constructor for XAML
|
||||
: this(DefaultCrsId)
|
||||
{
|
||||
}
|
||||
|
||||
public StereographicProjection(string crsId)
|
||||
{
|
||||
CrsId = crsId;
|
||||
}
|
||||
|
||||
public override Matrix RelativeTransform(double latitude, double longitude)
|
||||
{
|
||||
var p = GetProjectedPoint(latitude, longitude);
|
||||
var k = 2d / (1d + p.CosC); // p.157 (21-4), k0 == 1
|
||||
|
||||
return RelativeTransform(latitude, longitude, k, k);
|
||||
}
|
||||
|
||||
public override Point? LocationToMap(double latitude, double longitude)
|
||||
{
|
||||
var p = GetProjectedPoint(latitude, longitude);
|
||||
var k = 2d / (1d + p.CosC); // p.157 (21-4), k0 == 1
|
||||
|
||||
return new Point(EquatorialRadius * k * p.X, EquatorialRadius * k * p.Y); // p.157 (21-2/3)
|
||||
}
|
||||
|
||||
public override Location MapToLocation(double x, double y)
|
||||
{
|
||||
var rho = Math.Sqrt(x * x + y * y);
|
||||
var c = 2d * Math.Atan(rho / (2d * EquatorialRadius)); // p.159 (21-15), k0 == 1
|
||||
|
||||
return GetLocation(x, y, rho, Math.Sin(c));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -100,7 +100,7 @@ namespace MapControl
|
|||
return transform;
|
||||
}
|
||||
|
||||
public override Point? LocationToMap(double latitude, double longitude)
|
||||
public override Point LocationToMap(double latitude, double longitude)
|
||||
{
|
||||
// φ
|
||||
var phi = latitude * Math.PI / 180d;
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ namespace MapControl
|
|||
return transform;
|
||||
}
|
||||
|
||||
public override Point? LocationToMap(double latitude, double longitude)
|
||||
public override Point LocationToMap(double latitude, double longitude)
|
||||
{
|
||||
var phi = latitude * Math.PI / 180d;
|
||||
var M = MeridianDistance(phi);
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ namespace MapControl
|
|||
return new Matrix(k, 0d, 0d, k, 0d, 0d);
|
||||
}
|
||||
|
||||
public override Point? LocationToMap(double latitude, double longitude)
|
||||
public override Point LocationToMap(double latitude, double longitude)
|
||||
{
|
||||
return new Point(
|
||||
MeterPerDegree * longitude,
|
||||
|
|
|
|||
|
|
@ -1,27 +0,0 @@
|
|||
namespace MapControl
|
||||
{
|
||||
/// <summary>
|
||||
/// WGS84 Auto Transverse Mercator Projection - AUTO2:42002.
|
||||
/// CentralMeridian is automatically set to Center.Longitude.
|
||||
/// </summary>
|
||||
public class Wgs84AutoTmProjection : TransverseMercatorProjection
|
||||
{
|
||||
public const string DefaultCrsId = "AUTO2:42002";
|
||||
|
||||
public Wgs84AutoTmProjection() // parameterless constructor for XAML
|
||||
: this(DefaultCrsId)
|
||||
{
|
||||
}
|
||||
|
||||
public Wgs84AutoTmProjection(string crsId)
|
||||
{
|
||||
CrsId = crsId;
|
||||
EnableCenterUpdates();
|
||||
}
|
||||
|
||||
protected override void CenterChanged()
|
||||
{
|
||||
CentralMeridian = Center.Longitude;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,51 +0,0 @@
|
|||
using System;
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
/// <summary>
|
||||
/// WGS84 Universal Transverse Mercator Projection - AUTO2:42002.
|
||||
/// Zone and Hemisphere are automatically set according to the projection's Center.
|
||||
/// If the CRS identifier passed to the constructor is null or empty, appropriate
|
||||
/// values from EPSG:32601 to EPSG:32660 and EPSG:32701 to EPSG:32760 are used.
|
||||
/// </summary>
|
||||
public class Wgs84AutoUtmProjection : Wgs84UtmProjection
|
||||
{
|
||||
public const string DefaultCrsId = "AUTO2:42001";
|
||||
|
||||
private readonly string autoCrsId;
|
||||
|
||||
public Wgs84AutoUtmProjection() // parameterless constructor for XAML
|
||||
: this(DefaultCrsId)
|
||||
{
|
||||
}
|
||||
|
||||
public Wgs84AutoUtmProjection(string crsId)
|
||||
: base(31, Hemisphere.North)
|
||||
{
|
||||
autoCrsId = crsId;
|
||||
|
||||
if (!string.IsNullOrEmpty(autoCrsId))
|
||||
{
|
||||
CrsId = autoCrsId;
|
||||
}
|
||||
|
||||
EnableCenterUpdates();
|
||||
}
|
||||
|
||||
protected override void CenterChanged()
|
||||
{
|
||||
var zone = (int)Math.Floor(Center.Longitude / 6d) + 31;
|
||||
var hemisphere = Center.Latitude >= 0d ? Hemisphere.North : Hemisphere.South;
|
||||
|
||||
if (Zone != zone || Hemisphere != hemisphere)
|
||||
{
|
||||
SetZone(zone, hemisphere);
|
||||
|
||||
if (!string.IsNullOrEmpty(autoCrsId))
|
||||
{
|
||||
CrsId = autoCrsId;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -29,7 +29,7 @@ namespace MapControl
|
|||
SetZone(zone, hemisphere);
|
||||
}
|
||||
|
||||
protected void SetZone(int zone, Hemisphere hemisphere)
|
||||
public void SetZone(int zone, Hemisphere hemisphere)
|
||||
{
|
||||
if (zone < FirstZone || zone > LastZone)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -249,7 +249,7 @@ namespace MapControl
|
|||
{ "LAYERS", RequestLayers ?? AvailableLayers?.FirstOrDefault() ?? "" },
|
||||
{ "STYLES", RequestStyles ?? "" },
|
||||
{ "FORMAT", "image/png" },
|
||||
{ "CRS", GetCrsValue() },
|
||||
{ "CRS", ParentMap.MapProjection.CrsId },
|
||||
{ "BBOX", GetBboxValue(bbox) },
|
||||
{ "WIDTH", Math.Ceiling(width).ToString("F0") },
|
||||
{ "HEIGHT", Math.Ceiling(height).ToString("F0") }
|
||||
|
|
@ -285,7 +285,7 @@ namespace MapControl
|
|||
{ "LAYERS", RequestLayers ?? AvailableLayers?.FirstOrDefault() ?? "" },
|
||||
{ "STYLES", RequestStyles ?? "" },
|
||||
{ "INFO_FORMAT", format },
|
||||
{ "CRS", GetCrsValue() },
|
||||
{ "CRS", ParentMap.MapProjection.CrsId },
|
||||
{ "BBOX", GetBboxValue(bbox) },
|
||||
{ "WIDTH", Math.Ceiling(width).ToString("F0") },
|
||||
{ "HEIGHT", Math.Ceiling(height).ToString("F0") },
|
||||
|
|
@ -322,28 +322,6 @@ namespace MapControl
|
|||
return new Uri(ServiceUri.GetLeftPart(UriPartial.Path) + "?" + query);
|
||||
}
|
||||
|
||||
protected virtual string GetCrsValue()
|
||||
{
|
||||
var projection = ParentMap.MapProjection;
|
||||
var crs = projection.CrsId;
|
||||
|
||||
if (crs.StartsWith("AUTO2:") || crs.StartsWith("AUTO:"))
|
||||
{
|
||||
var lon = 0d;
|
||||
var lat = 0d; ;
|
||||
|
||||
if (projection.Center != null)
|
||||
{
|
||||
lon = projection.Center.Longitude;
|
||||
lat = projection.Center.Latitude;
|
||||
}
|
||||
|
||||
crs = string.Format(CultureInfo.InvariantCulture, "{0},1,{1:0.########},{2:0.########}", crs, lon, lat);
|
||||
}
|
||||
|
||||
return crs;
|
||||
}
|
||||
|
||||
protected virtual string GetBboxValue(Rect bbox)
|
||||
{
|
||||
var crs = ParentMap.MapProjection.CrsId;
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ namespace MapControl
|
|||
return new Matrix(k, 0d, 0d, k, 0d, 0d);
|
||||
}
|
||||
|
||||
public override Point? LocationToMap(double latitude, double longitude)
|
||||
public override Point LocationToMap(double latitude, double longitude)
|
||||
{
|
||||
return new Point(
|
||||
MeterPerDegree * longitude,
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ namespace MapControl
|
|||
FrameworkPropertyMetadataOptions.AffectsParentArrange);
|
||||
|
||||
public static readonly DependencyProperty MapRectProperty =
|
||||
DependencyPropertyHelper.RegisterAttached<MapRect>("MapRect", typeof(MapPanel), null,
|
||||
DependencyPropertyHelper.RegisterAttached<Rect?>("MapRect", typeof(MapPanel), null,
|
||||
FrameworkPropertyMetadataOptions.AffectsParentArrange);
|
||||
|
||||
public static MapBase GetParentMap(FrameworkElement element)
|
||||
|
|
|
|||
|
|
@ -36,10 +36,7 @@ namespace MapControl
|
|||
|
||||
private void AddPolylinePoints(StreamGeometryContext context, IEnumerable<Location> locations, double longitudeOffset, bool closed)
|
||||
{
|
||||
var points = locations
|
||||
.Select(location => LocationToView(location, longitudeOffset))
|
||||
.Where(point => point.HasValue)
|
||||
.Select(point => point.Value);
|
||||
var points = locations.Select(location => LocationToView(location, longitudeOffset));
|
||||
|
||||
if (points.Any())
|
||||
{
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ namespace MapControl
|
|||
(element, oldValue, newValue) => (element.Parent as MapPanel)?.InvalidateArrange());
|
||||
|
||||
public static readonly DependencyProperty MapRectProperty =
|
||||
DependencyPropertyHelper.RegisterAttached<MapRect>("MapRect", typeof(MapPanel), null,
|
||||
DependencyPropertyHelper.RegisterAttached<Rect?>("MapRect", typeof(MapPanel), null,
|
||||
(element, oldValue, newValue) => (element.Parent as MapPanel)?.InvalidateArrange());
|
||||
|
||||
public static void InitMapElement(FrameworkElement element)
|
||||
|
|
|
|||
|
|
@ -42,10 +42,7 @@ namespace MapControl
|
|||
|
||||
private void AddPolylinePoints(PathFigureCollection figures, IEnumerable<Location> locations, double longitudeOffset, bool closed)
|
||||
{
|
||||
var points = locations
|
||||
.Select(location => LocationToView(location, longitudeOffset))
|
||||
.Where(point => point.HasValue)
|
||||
.Select(point => point.Value);
|
||||
var points = locations.Select(location => LocationToView(location, longitudeOffset));
|
||||
|
||||
if (points.Any())
|
||||
{
|
||||
|
|
|
|||
|
|
@ -76,22 +76,16 @@ namespace MapControl.Projections
|
|||
return new Matrix(1d, 0d, 0d, 1d, 0d, 0d);
|
||||
}
|
||||
|
||||
public override Point? LocationToMap(double latitude, double longitude)
|
||||
public override Point LocationToMap(double latitude, double longitude)
|
||||
{
|
||||
if (LocationToMapTransform == null)
|
||||
{
|
||||
throw new InvalidOperationException("The CoordinateSystem property is not set.");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var coordinate = LocationToMapTransform.Transform([longitude, latitude]);
|
||||
return new Point(coordinate[0], coordinate[1]);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
var coordinate = LocationToMapTransform.Transform([longitude, latitude]);
|
||||
|
||||
return new Point(coordinate[0], coordinate[1]);
|
||||
}
|
||||
|
||||
public override Location MapToLocation(double x, double y)
|
||||
|
|
@ -101,15 +95,9 @@ namespace MapControl.Projections
|
|||
throw new InvalidOperationException("The CoordinateSystem property is not set.");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var coordinate = MapToLocationTransform.Transform([x, y]);
|
||||
return new Location(coordinate[1], coordinate[0]);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
var coordinate = MapToLocationTransform.Transform([x, y]);
|
||||
|
||||
return new Location(coordinate[1], coordinate[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,9 +32,6 @@ namespace MapControl.Projections
|
|||
MapControl.WorldMercatorProjection.DefaultCrsId => new WorldMercatorProjection(),
|
||||
MapControl.Wgs84UpsNorthProjection.DefaultCrsId => new Wgs84UpsNorthProjection(),
|
||||
MapControl.Wgs84UpsSouthProjection.DefaultCrsId => new Wgs84UpsSouthProjection(),
|
||||
MapControl.Wgs84AutoUtmProjection.DefaultCrsId => new Wgs84AutoUtmProjection(),
|
||||
MapControl.OrthographicProjection.DefaultCrsId => new Wgs84OrthographicProjection(),
|
||||
MapControl.StereographicProjection.DefaultCrsId => new Wgs84StereographicProjection(),
|
||||
_ => GetProjectionFromEpsgCode(crsId) ?? base.GetProjection(crsId)
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,46 +0,0 @@
|
|||
using System;
|
||||
|
||||
namespace MapControl.Projections
|
||||
{
|
||||
/// <summary>
|
||||
/// WGS84 Universal Transverse Mercator Projection with automatic zone selection from
|
||||
/// the projection center. If the CRS identifier passed to the constructor is null or empty,
|
||||
/// appropriate values from EPSG:32601 to EPSG:32660 and EPSG:32701 to EPSG:32760 are used.
|
||||
/// </summary>
|
||||
public class Wgs84AutoUtmProjection : Wgs84UtmProjection
|
||||
{
|
||||
private readonly string autoCrsId;
|
||||
|
||||
public Wgs84AutoUtmProjection() // parameterless constructor for XAML
|
||||
: this(MapControl.Wgs84AutoUtmProjection.DefaultCrsId)
|
||||
{
|
||||
}
|
||||
|
||||
public Wgs84AutoUtmProjection(string crsId)
|
||||
: base(31, Hemisphere.North)
|
||||
{
|
||||
autoCrsId = crsId;
|
||||
|
||||
if (!string.IsNullOrEmpty(autoCrsId))
|
||||
{
|
||||
CrsId = autoCrsId;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void CenterChanged()
|
||||
{
|
||||
var zone = (int)Math.Floor(Center.Longitude / 6d) + 31;
|
||||
var hemisphere = Center.Latitude >= 0d ? Hemisphere.North : Hemisphere.South;
|
||||
|
||||
if (Zone != zone || Hemisphere != hemisphere)
|
||||
{
|
||||
SetZone(zone, hemisphere);
|
||||
|
||||
if (!string.IsNullOrEmpty(autoCrsId))
|
||||
{
|
||||
CrsId = autoCrsId;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
using System.Globalization;
|
||||
#if WPF
|
||||
using System.Windows.Media;
|
||||
#endif
|
||||
|
||||
namespace MapControl.Projections
|
||||
{
|
||||
/// <summary>
|
||||
/// Spherical Orthographic Projection - AUTO2:42003.
|
||||
/// See "Map Projections - A Working Manual" (https://pubs.usgs.gov/publication/pp1395), p.148-150.
|
||||
/// </summary>
|
||||
public class Wgs84OrthographicProjection : ProjNetMapProjection
|
||||
{
|
||||
public Wgs84OrthographicProjection()
|
||||
{
|
||||
EnableCenterUpdates();
|
||||
}
|
||||
|
||||
protected override void CenterChanged()
|
||||
{
|
||||
var wktFormat =
|
||||
"PROJCS[\"WGS 84 / World Mercator\"," +
|
||||
WktConstants.GeogCsWgs84 + "," +
|
||||
"PROJECTION[\"Orthographic\"]," +
|
||||
"PARAMETER[\"latitude_of_origin\",{0:0.########}]," +
|
||||
"PARAMETER[\"central_meridian\",{1:0.########}]," +
|
||||
"UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]]," +
|
||||
"AXIS[\"Easting\",EAST]," +
|
||||
"AXIS[\"Northing\",NORTH]" +
|
||||
"AUTHORITY[\"AUTO2\",\"42003\"]]";
|
||||
|
||||
CoordinateSystemWkt = string.Format(
|
||||
CultureInfo.InvariantCulture, wktFormat, Center.Latitude, Center.Longitude);
|
||||
}
|
||||
|
||||
public override Matrix RelativeTransform(double latitude, double longitude)
|
||||
{
|
||||
var p = new AzimuthalProjection.ProjectedPoint(Center.Latitude, Center.Longitude, latitude, longitude);
|
||||
(var scaleX, var scaleY) = p.RelativeScale(p.CosC, 1d); // p.149 (20-5), k == 1
|
||||
|
||||
return RelativeTransform(latitude, longitude, scaleX, scaleY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
using System.Globalization;
|
||||
#if WPF
|
||||
using System.Windows.Media;
|
||||
#endif
|
||||
|
||||
namespace MapControl.Projections
|
||||
{
|
||||
/// <summary>
|
||||
/// Spherical Stereographic Projection - AUTO2:97002.
|
||||
/// See "Map Projections - A Working Manual" (https://pubs.usgs.gov/publication/pp1395), p.157-160.
|
||||
/// </summary>
|
||||
public class Wgs84StereographicProjection : ProjNetMapProjection
|
||||
{
|
||||
public Wgs84StereographicProjection()
|
||||
{
|
||||
EnableCenterUpdates();
|
||||
}
|
||||
|
||||
protected override void CenterChanged()
|
||||
{
|
||||
var wktFormat =
|
||||
"PROJCS[\"WGS 84 / World Mercator\"," +
|
||||
WktConstants.GeogCsWgs84 + "," +
|
||||
"PROJECTION[\"Oblique_Stereographic\"]," +
|
||||
"PARAMETER[\"latitude_of_origin\",{0:0.########}]," +
|
||||
"PARAMETER[\"central_meridian\",{1:0.########}]," +
|
||||
"UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]]," +
|
||||
"AXIS[\"Easting\",EAST]," +
|
||||
"AXIS[\"Northing\",NORTH]" +
|
||||
"AUTHORITY[\"AUTO2\",\"97002\"]]";
|
||||
|
||||
CoordinateSystemWkt = string.Format(
|
||||
CultureInfo.InvariantCulture, wktFormat, Center.Latitude, Center.Longitude);
|
||||
}
|
||||
|
||||
public override Matrix RelativeTransform(double latitude, double longitude)
|
||||
{
|
||||
var p = new AzimuthalProjection.ProjectedPoint(Center.Latitude, Center.Longitude, latitude, longitude);
|
||||
var k = 2d / (1d + p.CosC); // p.157 (21-4), k0 == 1
|
||||
|
||||
return RelativeTransform(latitude, longitude, k, k);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -23,9 +23,7 @@ namespace MapControl.Projections
|
|||
|
||||
public override Matrix RelativeTransform(double latitude, double longitude)
|
||||
{
|
||||
var k = PolarStereographicProjection.RelativeScale(Hemisphere.North, Wgs84Flattening, 0.994, latitude);
|
||||
|
||||
return RelativeTransform(latitude, longitude, k, k);
|
||||
return PolarStereographicProjection.RelativeScale(Hemisphere.North, Wgs84Flattening, 0.994, latitude, longitude);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -48,9 +46,7 @@ namespace MapControl.Projections
|
|||
|
||||
public override Matrix RelativeTransform(double latitude, double longitude)
|
||||
{
|
||||
var k = PolarStereographicProjection.RelativeScale(Hemisphere.South, Wgs84Flattening, 0.994, latitude);
|
||||
|
||||
return RelativeTransform(latitude, longitude, k, k);
|
||||
return PolarStereographicProjection.RelativeScale(Hemisphere.North, Wgs84Flattening, 0.994, latitude, longitude);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue