mirror of
https://github.com/ClemensFischer/XAML-Map-Control.git
synced 2025-12-06 07:12:04 +01:00
Version 4.12.1 Added AutoEquirectangularProjection
This commit is contained in:
parent
0a3ae81117
commit
bd9a16e921
|
|
@ -46,7 +46,7 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform">
|
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform">
|
||||||
<Version>6.2.2</Version>
|
<Version>6.2.8</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
|
||||||
|
|
@ -54,10 +54,10 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.Data.SQLite">
|
<PackageReference Include="Microsoft.Data.SQLite">
|
||||||
<Version>2.2.0</Version>
|
<Version>2.2.3</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform">
|
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform">
|
||||||
<Version>6.2.2</Version>
|
<Version>6.2.8</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@
|
||||||
// Licensed under the Microsoft Public License (Ms-PL)
|
// Licensed under the Microsoft Public License (Ms-PL)
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Globalization;
|
|
||||||
#if WINDOWS_UWP
|
#if WINDOWS_UWP
|
||||||
using Windows.Foundation;
|
using Windows.Foundation;
|
||||||
#else
|
#else
|
||||||
|
|
@ -14,8 +13,6 @@ namespace MapControl
|
||||||
{
|
{
|
||||||
public class AutoEquirectangularProjection : MapProjection
|
public class AutoEquirectangularProjection : MapProjection
|
||||||
{
|
{
|
||||||
public Location ProjectionCenter { get; private set; } = new Location();
|
|
||||||
|
|
||||||
public AutoEquirectangularProjection()
|
public AutoEquirectangularProjection()
|
||||||
: this("AUTO2:42004")
|
: this("AUTO2:42004")
|
||||||
{
|
{
|
||||||
|
|
@ -29,44 +26,20 @@ namespace MapControl
|
||||||
|
|
||||||
public override Point LocationToPoint(Location location)
|
public override Point LocationToPoint(Location location)
|
||||||
{
|
{
|
||||||
var xScale = MetersPerDegree * Math.Cos(ProjectionCenter.Latitude * Math.PI / 180d);
|
var xScale = Wgs84MetersPerDegree * Math.Cos(ProjectionCenter.Latitude * Math.PI / 180d);
|
||||||
|
|
||||||
return new Point(
|
return new Point(
|
||||||
xScale * (location.Longitude - ProjectionCenter.Longitude),
|
xScale * (location.Longitude - ProjectionCenter.Longitude),
|
||||||
MetersPerDegree * location.Latitude);
|
Wgs84MetersPerDegree * location.Latitude);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Location PointToLocation(Point point)
|
public override Location PointToLocation(Point point)
|
||||||
{
|
{
|
||||||
var xScale = MetersPerDegree * Math.Cos(ProjectionCenter.Latitude * Math.PI / 180d);
|
var xScale = Wgs84MetersPerDegree * Math.Cos(ProjectionCenter.Latitude * Math.PI / 180d);
|
||||||
|
|
||||||
return new Location(
|
return new Location(
|
||||||
point.Y / MetersPerDegree,
|
point.Y / Wgs84MetersPerDegree,
|
||||||
point.X / xScale + ProjectionCenter.Longitude);
|
point.X / xScale + ProjectionCenter.Longitude);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void SetViewportTransform(Location projectionCenter, Location mapCenter, Point viewportCenter, double zoomLevel, double heading)
|
|
||||||
{
|
|
||||||
ProjectionCenter = projectionCenter;
|
|
||||||
|
|
||||||
base.SetViewportTransform(projectionCenter, mapCenter, viewportCenter, zoomLevel, heading);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string WmsQueryParameters(BoundingBox boundingBox)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(CrsId))
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var rect = BoundingBoxToRect(boundingBox);
|
|
||||||
var width = (int)Math.Round(ViewportScale * rect.Width);
|
|
||||||
var height = (int)Math.Round(ViewportScale * rect.Height);
|
|
||||||
|
|
||||||
return string.Format(CultureInfo.InvariantCulture,
|
|
||||||
"CRS={0},1,{1},{2}&BBOX={3},{4},{5},{6}&WIDTH={7}&HEIGHT={8}",
|
|
||||||
CrsId, ProjectionCenter.Longitude, ProjectionCenter.Latitude,
|
|
||||||
rect.X, rect.Y, (rect.X + rect.Width), (rect.Y + rect.Height), width, height);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,9 +35,9 @@ namespace MapControl
|
||||||
|
|
||||||
GetAzimuthDistance(ProjectionCenter, location, out azimuth, out distance);
|
GetAzimuthDistance(ProjectionCenter, location, out azimuth, out distance);
|
||||||
|
|
||||||
distance *= TrueScale * 180d / Math.PI;
|
var mapDistance = distance * TrueScale * 180d / Math.PI;
|
||||||
|
|
||||||
return new Point(distance * Math.Sin(azimuth), distance * Math.Cos(azimuth));
|
return new Point(mapDistance * Math.Sin(azimuth), mapDistance * Math.Cos(azimuth));
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Location PointToLocation(Point point)
|
public override Location PointToLocation(Point point)
|
||||||
|
|
@ -48,7 +48,9 @@ namespace MapControl
|
||||||
}
|
}
|
||||||
|
|
||||||
var azimuth = Math.Atan2(point.X, point.Y);
|
var azimuth = Math.Atan2(point.X, point.Y);
|
||||||
var distance = Math.Sqrt(point.X * point.X + point.Y * point.Y) / (TrueScale * 180d / Math.PI);
|
var mapDistance = Math.Sqrt(point.X * point.X + point.Y * point.Y);
|
||||||
|
|
||||||
|
var distance = mapDistance / (TrueScale * 180d / Math.PI);
|
||||||
|
|
||||||
return GetLocation(ProjectionCenter, azimuth, distance);
|
return GetLocation(ProjectionCenter, azimuth, distance);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@
|
||||||
// Licensed under the Microsoft Public License (Ms-PL)
|
// Licensed under the Microsoft Public License (Ms-PL)
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Globalization;
|
|
||||||
#if WINDOWS_UWP
|
#if WINDOWS_UWP
|
||||||
using Windows.Foundation;
|
using Windows.Foundation;
|
||||||
#else
|
#else
|
||||||
|
|
@ -17,8 +16,6 @@ namespace MapControl
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract class AzimuthalProjection : MapProjection
|
public abstract class AzimuthalProjection : MapProjection
|
||||||
{
|
{
|
||||||
public Location ProjectionCenter { get; private set; } = new Location();
|
|
||||||
|
|
||||||
public override Rect BoundingBoxToRect(BoundingBox boundingBox)
|
public override Rect BoundingBoxToRect(BoundingBox boundingBox)
|
||||||
{
|
{
|
||||||
var cbbox = boundingBox as CenteredBoundingBox;
|
var cbbox = boundingBox as CenteredBoundingBox;
|
||||||
|
|
@ -42,30 +39,6 @@ namespace MapControl
|
||||||
return new CenteredBoundingBox(center, rect.Width, rect.Height); // width and height in meters
|
return new CenteredBoundingBox(center, rect.Width, rect.Height); // width and height in meters
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void SetViewportTransform(Location projectionCenter, Location mapCenter, Point viewportCenter, double zoomLevel, double heading)
|
|
||||||
{
|
|
||||||
ProjectionCenter = projectionCenter;
|
|
||||||
|
|
||||||
base.SetViewportTransform(projectionCenter, mapCenter, viewportCenter, zoomLevel, heading);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string WmsQueryParameters(BoundingBox boundingBox)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(CrsId))
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var rect = BoundingBoxToRect(boundingBox);
|
|
||||||
var width = (int)Math.Round(ViewportScale * rect.Width);
|
|
||||||
var height = (int)Math.Round(ViewportScale * rect.Height);
|
|
||||||
|
|
||||||
return string.Format(CultureInfo.InvariantCulture,
|
|
||||||
"CRS={0},1,{1},{2}&BBOX={3},{4},{5},{6}&WIDTH={7}&HEIGHT={8}",
|
|
||||||
CrsId, ProjectionCenter.Longitude, ProjectionCenter.Latitude,
|
|
||||||
rect.X, rect.Y, (rect.X + rect.Width), (rect.Y + rect.Height), width, height);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Calculates azimuth and spherical distance in radians from location1 to location2.
|
/// Calculates azimuth and spherical distance in radians from location1 to location2.
|
||||||
/// The returned distance has to be multiplied with an appropriate earth radius.
|
/// The returned distance has to be multiplied with an appropriate earth radius.
|
||||||
|
|
|
||||||
|
|
@ -30,8 +30,8 @@ namespace MapControl
|
||||||
public override Vector GetMapScale(Location location)
|
public override Vector GetMapScale(Location location)
|
||||||
{
|
{
|
||||||
return new Vector(
|
return new Vector(
|
||||||
ViewportScale / (MetersPerDegree * Math.Cos(location.Latitude * Math.PI / 180d)),
|
ViewportScale / (Wgs84MetersPerDegree * Math.Cos(location.Latitude * Math.PI / 180d)),
|
||||||
ViewportScale / MetersPerDegree);
|
ViewportScale / Wgs84MetersPerDegree);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Point LocationToPoint(Location location)
|
public override Point LocationToPoint(Location location)
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,7 @@ namespace MapControl
|
||||||
|
|
||||||
var azimuth = Math.Atan2(point.X, point.Y);
|
var azimuth = Math.Atan2(point.X, point.Y);
|
||||||
var mapDistance = Math.Sqrt(point.X * point.X + point.Y * point.Y);
|
var mapDistance = Math.Sqrt(point.X * point.X + point.Y * point.Y);
|
||||||
|
|
||||||
var distance = Math.Atan(mapDistance / (TrueScale * 180d / Math.PI));
|
var distance = Math.Atan(mapDistance / (TrueScale * 180d / Math.PI));
|
||||||
|
|
||||||
return GetLocation(ProjectionCenter, azimuth, distance);
|
return GetLocation(ProjectionCenter, azimuth, distance);
|
||||||
|
|
|
||||||
|
|
@ -62,20 +62,27 @@ namespace MapControl
|
||||||
return string.Format(CultureInfo.InvariantCulture, "{0:F5},{1:F5}", latitude, longitude);
|
return string.Format(CultureInfo.InvariantCulture, "{0:F5},{1:F5}", latitude, longitude);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Location Parse(string s)
|
public static Location Parse(string locationString)
|
||||||
{
|
{
|
||||||
var values = s.Split(new char[] { ',' });
|
Location location = null;
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(locationString))
|
||||||
|
{
|
||||||
|
var values = locationString.Split(new char[] { ',' });
|
||||||
|
|
||||||
if (values.Length != 2)
|
if (values.Length != 2)
|
||||||
{
|
{
|
||||||
throw new FormatException("Location string must be a comma-separated pair of double values.");
|
throw new FormatException("Location string must be a comma-separated pair of double values.");
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Location(
|
location = new Location(
|
||||||
double.Parse(values[0], NumberStyles.Float, CultureInfo.InvariantCulture),
|
double.Parse(values[0], NumberStyles.Float, CultureInfo.InvariantCulture),
|
||||||
double.Parse(values[1], NumberStyles.Float, CultureInfo.InvariantCulture));
|
double.Parse(values[1], NumberStyles.Float, CultureInfo.InvariantCulture));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return location;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Normalizes a longitude to a value in the interval [-180..180].
|
/// Normalizes a longitude to a value in the interval [-180..180].
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,6 @@ namespace MapControl
|
||||||
nameof(MapForeground), typeof(Brush), typeof(MapImageLayer), new PropertyMetadata(null));
|
nameof(MapForeground), typeof(Brush), typeof(MapImageLayer), new PropertyMetadata(null));
|
||||||
|
|
||||||
private readonly DispatcherTimer updateTimer;
|
private readonly DispatcherTimer updateTimer;
|
||||||
private BoundingBox boundingBox;
|
|
||||||
private bool updateInProgress;
|
private bool updateInProgress;
|
||||||
|
|
||||||
public MapImageLayer()
|
public MapImageLayer()
|
||||||
|
|
@ -180,9 +179,14 @@ namespace MapControl
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns an ImageSource for the specified bounding box.
|
/// The current BoundingBox
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected abstract Task<ImageSource> GetImageAsync(BoundingBox boundingBox);
|
public BoundingBox BoundingBox { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns an ImageSource for the current BoundingBox.
|
||||||
|
/// </summary>
|
||||||
|
protected abstract Task<ImageSource> GetImageAsync();
|
||||||
|
|
||||||
protected override void OnViewportChanged(ViewportChangedEventArgs e)
|
protected override void OnViewportChanged(ViewportChangedEventArgs e)
|
||||||
{
|
{
|
||||||
|
|
@ -228,11 +232,11 @@ namespace MapControl
|
||||||
|
|
||||||
ImageSource imageSource = null;
|
ImageSource imageSource = null;
|
||||||
|
|
||||||
if (boundingBox != null)
|
if (BoundingBox != null)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
imageSource = await GetImageAsync(boundingBox);
|
imageSource = await GetImageAsync();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|
@ -254,47 +258,47 @@ namespace MapControl
|
||||||
var y = (ParentMap.RenderSize.Height - height) / 2d;
|
var y = (ParentMap.RenderSize.Height - height) / 2d;
|
||||||
var rect = new Rect(x, y, width, height);
|
var rect = new Rect(x, y, width, height);
|
||||||
|
|
||||||
boundingBox = ParentMap.MapProjection.ViewportRectToBoundingBox(rect);
|
BoundingBox = ParentMap.MapProjection.ViewportRectToBoundingBox(rect);
|
||||||
|
|
||||||
if (boundingBox != null)
|
if (BoundingBox != null)
|
||||||
{
|
{
|
||||||
if (!double.IsNaN(MinLatitude) && boundingBox.South < MinLatitude)
|
if (!double.IsNaN(MinLatitude) && BoundingBox.South < MinLatitude)
|
||||||
{
|
{
|
||||||
boundingBox.South = MinLatitude;
|
BoundingBox.South = MinLatitude;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!double.IsNaN(MinLongitude) && boundingBox.West < MinLongitude)
|
if (!double.IsNaN(MinLongitude) && BoundingBox.West < MinLongitude)
|
||||||
{
|
{
|
||||||
boundingBox.West = MinLongitude;
|
BoundingBox.West = MinLongitude;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!double.IsNaN(MaxLatitude) && boundingBox.North > MaxLatitude)
|
if (!double.IsNaN(MaxLatitude) && BoundingBox.North > MaxLatitude)
|
||||||
{
|
{
|
||||||
boundingBox.North = MaxLatitude;
|
BoundingBox.North = MaxLatitude;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!double.IsNaN(MaxLongitude) && boundingBox.East > MaxLongitude)
|
if (!double.IsNaN(MaxLongitude) && BoundingBox.East > MaxLongitude)
|
||||||
{
|
{
|
||||||
boundingBox.East = MaxLongitude;
|
BoundingBox.East = MaxLongitude;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!double.IsNaN(MaxBoundingBoxWidth) && boundingBox.Width > MaxBoundingBoxWidth)
|
if (!double.IsNaN(MaxBoundingBoxWidth) && BoundingBox.Width > MaxBoundingBoxWidth)
|
||||||
{
|
{
|
||||||
var d = (boundingBox.Width - MaxBoundingBoxWidth) / 2d;
|
var d = (BoundingBox.Width - MaxBoundingBoxWidth) / 2d;
|
||||||
boundingBox.West += d;
|
BoundingBox.West += d;
|
||||||
boundingBox.East -= d;
|
BoundingBox.East -= d;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AdjustBoundingBox(double longitudeOffset)
|
private void AdjustBoundingBox(double longitudeOffset)
|
||||||
{
|
{
|
||||||
if (Math.Abs(longitudeOffset) > 180d && boundingBox != null)
|
if (Math.Abs(longitudeOffset) > 180d && BoundingBox != null)
|
||||||
{
|
{
|
||||||
var offset = 360d * Math.Sign(longitudeOffset);
|
var offset = 360d * Math.Sign(longitudeOffset);
|
||||||
|
|
||||||
boundingBox.West += offset;
|
BoundingBox.West += offset;
|
||||||
boundingBox.East += offset;
|
BoundingBox.East += offset;
|
||||||
|
|
||||||
foreach (var element in Children.OfType<FrameworkElement>())
|
foreach (var element in Children.OfType<FrameworkElement>())
|
||||||
{
|
{
|
||||||
|
|
@ -326,7 +330,7 @@ namespace MapControl
|
||||||
Children.Insert(1, topImage);
|
Children.Insert(1, topImage);
|
||||||
|
|
||||||
topImage.Source = imageSource;
|
topImage.Source = imageSource;
|
||||||
SetBoundingBox(topImage, boundingBox?.Clone());
|
SetBoundingBox(topImage, BoundingBox?.Clone());
|
||||||
|
|
||||||
topImage.BeginAnimation(OpacityProperty, new DoubleAnimation
|
topImage.BeginAnimation(OpacityProperty, new DoubleAnimation
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@
|
||||||
// Licensed under the Microsoft Public License (Ms-PL)
|
// Licensed under the Microsoft Public License (Ms-PL)
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Globalization;
|
|
||||||
#if WINDOWS_UWP
|
#if WINDOWS_UWP
|
||||||
using Windows.Foundation;
|
using Windows.Foundation;
|
||||||
using Windows.UI.Xaml.Media;
|
using Windows.UI.Xaml.Media;
|
||||||
|
|
@ -26,7 +25,7 @@ namespace MapControl
|
||||||
public const double Wgs84Flattening = 1d / 298.257223563;
|
public const double Wgs84Flattening = 1d / 298.257223563;
|
||||||
public static readonly double Wgs84Eccentricity = Math.Sqrt((2d - Wgs84Flattening) * Wgs84Flattening);
|
public static readonly double Wgs84Eccentricity = Math.Sqrt((2d - Wgs84Flattening) * Wgs84Flattening);
|
||||||
|
|
||||||
public const double MetersPerDegree = Wgs84EquatorialRadius * Math.PI / 180d;
|
public const double Wgs84MetersPerDegree = Wgs84EquatorialRadius * Math.PI / 180d;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the WMS 1.3.0 CRS Identifier.
|
/// Gets or sets the WMS 1.3.0 CRS Identifier.
|
||||||
|
|
@ -47,13 +46,18 @@ namespace MapControl
|
||||||
/// Gets the scale factor from geographic to cartesian coordinates, on the line of true scale of a
|
/// Gets the scale factor from geographic to cartesian coordinates, on the line of true scale of a
|
||||||
/// cylindrical projection (usually the equator), or at the projection center of an azimuthal projection.
|
/// cylindrical projection (usually the equator), or at the projection center of an azimuthal projection.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public double TrueScale { get; protected set; } = MetersPerDegree;
|
public double TrueScale { get; protected set; } = Wgs84MetersPerDegree;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the absolute value of the minimum and maximum latitude that can be transformed.
|
/// Gets the absolute value of the minimum and maximum latitude that can be transformed.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public double MaxLatitude { get; protected set; } = 90d;
|
public double MaxLatitude { get; protected set; } = 90d;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the projection center. Only relevant for azimuthal pprojections.
|
||||||
|
/// </summary>
|
||||||
|
public Location ProjectionCenter { get; private set; } = new Location();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the transform matrix from cartesian map coordinates to viewport coordinates (pixels).
|
/// Gets the transform matrix from cartesian map coordinates to viewport coordinates (pixels).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -136,10 +140,11 @@ namespace MapControl
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sets ViewportScale and ViewportTransform values.
|
/// Sets ProjectionCenter, ViewportScale, ViewportTransform and InverseViewportTransform.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public virtual void SetViewportTransform(Location projectionCenter, Location mapCenter, Point viewportCenter, double zoomLevel, double heading)
|
public void SetViewportTransform(Location projectionCenter, Location mapCenter, Point viewportCenter, double zoomLevel, double heading)
|
||||||
{
|
{
|
||||||
|
ProjectionCenter = projectionCenter;
|
||||||
ViewportScale = Math.Pow(2d, zoomLevel) * PixelPerDegree / TrueScale;
|
ViewportScale = Math.Pow(2d, zoomLevel) * PixelPerDegree / TrueScale;
|
||||||
|
|
||||||
var center = LocationToPoint(mapCenter);
|
var center = LocationToPoint(mapCenter);
|
||||||
|
|
@ -150,27 +155,6 @@ namespace MapControl
|
||||||
InverseViewportTransform = matrix;
|
InverseViewportTransform = matrix;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a WMS query parameter string from the specified bounding box, e.g. "CRS=...&BBOX=...&WIDTH=...&HEIGHT=..."
|
|
||||||
/// </summary>
|
|
||||||
public virtual string WmsQueryParameters(BoundingBox boundingBox)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(CrsId))
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var format = CrsId == "EPSG:4326"
|
|
||||||
? "CRS={0}&BBOX={2},{1},{4},{3}&WIDTH={5}&HEIGHT={6}"
|
|
||||||
: "CRS={0}&BBOX={1},{2},{3},{4}&WIDTH={5}&HEIGHT={6}";
|
|
||||||
var rect = BoundingBoxToRect(boundingBox);
|
|
||||||
var width = (int)Math.Round(ViewportScale * rect.Width);
|
|
||||||
var height = (int)Math.Round(ViewportScale * rect.Height);
|
|
||||||
|
|
||||||
return string.Format(CultureInfo.InvariantCulture, format, CrsId,
|
|
||||||
rect.X, rect.Y, (rect.X + rect.Width), (rect.Y + rect.Height), width, height);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static Matrix CreateTransformMatrix(
|
internal static Matrix CreateTransformMatrix(
|
||||||
Point translation1, double scale, double rotation, Point translation2)
|
Point translation1, double scale, double rotation, Point translation2)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ namespace MapControl
|
||||||
|
|
||||||
GetAzimuthDistance(ProjectionCenter, location, out azimuth, out distance);
|
GetAzimuthDistance(ProjectionCenter, location, out azimuth, out distance);
|
||||||
|
|
||||||
var mapDistance = Math.Tan(distance / 2d) * TrueScale * 360d / Math.PI;
|
var mapDistance = Math.Tan(distance / 2d) * 2d * TrueScale * 180d / Math.PI;
|
||||||
|
|
||||||
return new Point(mapDistance * Math.Sin(azimuth), mapDistance * Math.Cos(azimuth));
|
return new Point(mapDistance * Math.Sin(azimuth), mapDistance * Math.Cos(azimuth));
|
||||||
}
|
}
|
||||||
|
|
@ -49,7 +49,8 @@ namespace MapControl
|
||||||
|
|
||||||
var azimuth = Math.Atan2(point.X, point.Y);
|
var azimuth = Math.Atan2(point.X, point.Y);
|
||||||
var mapDistance = Math.Sqrt(point.X * point.X + point.Y * point.Y);
|
var mapDistance = Math.Sqrt(point.X * point.X + point.Y * point.Y);
|
||||||
var distance = 2d * Math.Atan(mapDistance / (TrueScale * 360d / Math.PI));
|
|
||||||
|
var distance = 2d * Math.Atan(mapDistance / (2d * TrueScale * 180d / Math.PI));
|
||||||
|
|
||||||
return GetLocation(ProjectionCenter, azimuth, distance);
|
return GetLocation(ProjectionCenter, azimuth, distance);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -161,10 +161,10 @@ namespace MapControl
|
||||||
private string GetBoundingBoxUri(int x, int y, int zoomLevel)
|
private string GetBoundingBoxUri(int x, int y, int zoomLevel)
|
||||||
{
|
{
|
||||||
var tileSize = 360d / (1 << zoomLevel); // tile width in degrees
|
var tileSize = 360d / (1 << zoomLevel); // tile width in degrees
|
||||||
var west = MapProjection.MetersPerDegree * (x * tileSize - 180d);
|
var west = MapProjection.Wgs84MetersPerDegree * (x * tileSize - 180d);
|
||||||
var east = MapProjection.MetersPerDegree * ((x + 1) * tileSize - 180d);
|
var east = MapProjection.Wgs84MetersPerDegree * ((x + 1) * tileSize - 180d);
|
||||||
var south = MapProjection.MetersPerDegree * (180d - (y + 1) * tileSize);
|
var south = MapProjection.Wgs84MetersPerDegree * (180d - (y + 1) * tileSize);
|
||||||
var north = MapProjection.MetersPerDegree * (180d - y * tileSize);
|
var north = MapProjection.Wgs84MetersPerDegree * (180d - y * tileSize);
|
||||||
|
|
||||||
return uriFormat
|
return uriFormat
|
||||||
.Replace("{W}", west.ToString(CultureInfo.InvariantCulture))
|
.Replace("{W}", west.ToString(CultureInfo.InvariantCulture))
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using System.Globalization;
|
||||||
#if WINDOWS_UWP
|
#if WINDOWS_UWP
|
||||||
using Windows.Data.Xml.Dom;
|
using Windows.Data.Xml.Dom;
|
||||||
using Windows.UI.Xaml;
|
using Windows.UI.Xaml;
|
||||||
|
|
@ -37,6 +38,10 @@ namespace MapControl
|
||||||
nameof(Format), typeof(string), typeof(WmsImageLayer),
|
nameof(Format), typeof(string), typeof(WmsImageLayer),
|
||||||
new PropertyMetadata("image/png", async (o, e) => await ((WmsImageLayer)o).UpdateImageAsync()));
|
new PropertyMetadata("image/png", async (o, e) => await ((WmsImageLayer)o).UpdateImageAsync()));
|
||||||
|
|
||||||
|
public static readonly DependencyProperty CrsIdMapProperty = DependencyProperty.Register(
|
||||||
|
nameof(CrsIdMap), typeof(string), typeof(WmsImageLayer),
|
||||||
|
new PropertyMetadata(null, async (o, e) => await ((WmsImageLayer)o).CrsIdMapPropertyChanged((string)e.NewValue)));
|
||||||
|
|
||||||
public Uri ServiceUri
|
public Uri ServiceUri
|
||||||
{
|
{
|
||||||
get { return (Uri)GetValue(ServiceUriProperty); }
|
get { return (Uri)GetValue(ServiceUriProperty); }
|
||||||
|
|
@ -61,6 +66,17 @@ namespace MapControl
|
||||||
set { SetValue(FormatProperty, value); }
|
set { SetValue(FormatProperty, value); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string CrsIdMap
|
||||||
|
{
|
||||||
|
get { return (string)GetValue(CrsIdMapProperty); }
|
||||||
|
set { SetValue(CrsIdMapProperty, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
private Dictionary<string, string> crsIdMap;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a list of all layer names returned by a GetCapabilities response.
|
||||||
|
/// </summary>
|
||||||
public async Task<IList<string>> GetLayerNamesAsync()
|
public async Task<IList<string>> GetLayerNamesAsync()
|
||||||
{
|
{
|
||||||
IList<string> layerNames = null;
|
IList<string> layerNames = null;
|
||||||
|
|
@ -100,14 +116,24 @@ namespace MapControl
|
||||||
return layerNames;
|
return layerNames;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override async Task<ImageSource> GetImageAsync(BoundingBox boundingBox)
|
protected override async Task<ImageSource> GetImageAsync()
|
||||||
{
|
{
|
||||||
ImageSource imageSource = null;
|
var imageUri = GetImageUri();
|
||||||
var projectionParameters = ParentMap.MapProjection.WmsQueryParameters(boundingBox);
|
|
||||||
|
|
||||||
if (ServiceUri != null && !string.IsNullOrEmpty(projectionParameters))
|
return imageUri != null ? await ImageLoader.LoadImageAsync(imageUri) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a GetMap request URL for the current BoundingBox.
|
||||||
|
/// </summary>
|
||||||
|
protected virtual Uri GetImageUri()
|
||||||
{
|
{
|
||||||
var uri = GetRequestUri("GetMap&" + projectionParameters);
|
Uri imageUri = null;
|
||||||
|
var projection = ParentMap?.MapProjection;
|
||||||
|
|
||||||
|
if (ServiceUri != null && projection != null && !string.IsNullOrEmpty(projection.CrsId))
|
||||||
|
{
|
||||||
|
var uri = GetRequestUri("GetMap");
|
||||||
|
|
||||||
if (uri.IndexOf("LAYERS=", StringComparison.OrdinalIgnoreCase) < 0 && Layers != null)
|
if (uri.IndexOf("LAYERS=", StringComparison.OrdinalIgnoreCase) < 0 && Layers != null)
|
||||||
{
|
{
|
||||||
|
|
@ -124,10 +150,51 @@ namespace MapControl
|
||||||
uri += "&FORMAT=" + Format;
|
uri += "&FORMAT=" + Format;
|
||||||
}
|
}
|
||||||
|
|
||||||
imageSource = await ImageLoader.LoadImageAsync(new Uri(uri.Replace(" ", "%20")));
|
var rect = projection.BoundingBoxToRect(BoundingBox);
|
||||||
|
|
||||||
|
uri += "&" + GetBboxParameters(rect);
|
||||||
|
uri += "&WIDTH=" + (int)Math.Round(projection.ViewportScale * rect.Width);
|
||||||
|
uri += "&HEIGHT=" + (int)Math.Round(projection.ViewportScale * rect.Height);
|
||||||
|
|
||||||
|
imageUri = new Uri(uri.Replace(" ", "%20"));
|
||||||
}
|
}
|
||||||
|
|
||||||
return imageSource;
|
return imageUri;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a query substring for the projected bounding box, which contains the CRS and BBOX or equivalent parameters.
|
||||||
|
/// </summary>
|
||||||
|
protected virtual string GetBboxParameters(Rect bbox)
|
||||||
|
{
|
||||||
|
var crsId = GetCrsValue();
|
||||||
|
|
||||||
|
return string.Format(CultureInfo.InvariantCulture,
|
||||||
|
crsId == "EPSG:4326" ? "CRS={0}&BBOX={2},{1},{4},{3}" : "CRS={0}&BBOX={1},{2},{3},{4}",
|
||||||
|
crsId, bbox.X, bbox.Y, (bbox.X + bbox.Width), (bbox.Y + bbox.Height));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the effective value of the CRS query parameter.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
protected virtual string GetCrsValue()
|
||||||
|
{
|
||||||
|
var projection = ParentMap.MapProjection;
|
||||||
|
var crsId = projection.CrsId;
|
||||||
|
|
||||||
|
if (crsIdMap != null && !crsIdMap.TryGetValue(crsId, out crsId))
|
||||||
|
{
|
||||||
|
crsId = projection.CrsId;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (crsId.StartsWith("AUTO2:") || crsId.StartsWith("AUTO:"))
|
||||||
|
{
|
||||||
|
crsId = string.Format(CultureInfo.InvariantCulture, "{0},1,{1},{2}",
|
||||||
|
crsId, projection.ProjectionCenter.Longitude, projection.ProjectionCenter.Latitude);
|
||||||
|
}
|
||||||
|
|
||||||
|
return crsId;
|
||||||
}
|
}
|
||||||
|
|
||||||
private string GetRequestUri(string request)
|
private string GetRequestUri(string request)
|
||||||
|
|
@ -152,9 +219,31 @@ namespace MapControl
|
||||||
return uri + "REQUEST=" + request;
|
return uri + "REQUEST=" + request;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Task CrsIdMapPropertyChanged(string crsIdMapString)
|
||||||
|
{
|
||||||
|
crsIdMap = null;
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(crsIdMapString))
|
||||||
|
{
|
||||||
|
var entries = crsIdMapString.Split(new char[] { ' ', ',', ';' }, StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
|
||||||
|
if (entries.Length >= 2)
|
||||||
|
{
|
||||||
|
crsIdMap = new Dictionary<string, string>();
|
||||||
|
|
||||||
|
for (int i = 0; i < entries.Length - 1; i += 2)
|
||||||
|
{
|
||||||
|
crsIdMap[entries[i]] = entries[i + 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return UpdateImageAsync();
|
||||||
|
}
|
||||||
|
|
||||||
private static IEnumerable<XmlElement> ChildElements(XmlElement element, string name)
|
private static IEnumerable<XmlElement> ChildElements(XmlElement element, string name)
|
||||||
{
|
{
|
||||||
return element.ChildNodes.OfType<XmlElement>().Where(e => (string)e.LocalName == name);
|
return element.ChildNodes.OfType<XmlElement>().Where(e => e.LocalName == name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -175,7 +175,7 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform">
|
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform">
|
||||||
<Version>6.2.2</Version>
|
<Version>6.2.8</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,7 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform">
|
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform">
|
||||||
<Version>6.2.2</Version>
|
<Version>6.2.8</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,7 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform">
|
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform">
|
||||||
<Version>6.2.2</Version>
|
<Version>6.2.8</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="ProjNET4GeoAPI">
|
<PackageReference Include="ProjNET4GeoAPI">
|
||||||
<Version>1.4.1</Version>
|
<Version>1.4.1</Version>
|
||||||
|
|
|
||||||
|
|
@ -152,7 +152,7 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform">
|
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform">
|
||||||
<Version>6.2.2</Version>
|
<Version>6.2.8</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<PropertyGroup Condition=" '$(VisualStudioVersion)' == '' or '$(VisualStudioVersion)' < '14.0' ">
|
<PropertyGroup Condition=" '$(VisualStudioVersion)' == '' or '$(VisualStudioVersion)' < '14.0' ">
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue