mirror of
https://github.com/ClemensFischer/XAML-Map-Control.git
synced 2025-12-06 07:12:04 +01:00
Version 4.16.0. Improved MapProjection.
This commit is contained in:
parent
60e0093785
commit
12566506f8
|
|
@ -14,14 +14,8 @@ namespace MapControl
|
|||
public class AutoEquirectangularProjection : MapProjection
|
||||
{
|
||||
public AutoEquirectangularProjection()
|
||||
: this("AUTO2:42004")
|
||||
{
|
||||
}
|
||||
|
||||
public AutoEquirectangularProjection(string crsId)
|
||||
{
|
||||
CrsId = crsId;
|
||||
IsNormalCylindrical = true;
|
||||
CrsId = "AUTO2:42004";
|
||||
}
|
||||
|
||||
public override Point LocationToPoint(Location location)
|
||||
|
|
|
|||
|
|
@ -14,15 +14,7 @@ namespace MapControl
|
|||
/// </summary>
|
||||
public class AzimuthalEquidistantProjection : AzimuthalProjection
|
||||
{
|
||||
public AzimuthalEquidistantProjection()
|
||||
{
|
||||
// No known standard or de-facto standard CRS ID
|
||||
}
|
||||
|
||||
public AzimuthalEquidistantProjection(string crsId)
|
||||
{
|
||||
CrsId = crsId;
|
||||
}
|
||||
// No standard CRS ID
|
||||
|
||||
public override Point LocationToPoint(Location location)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -16,6 +16,11 @@ namespace MapControl
|
|||
/// </summary>
|
||||
public abstract class AzimuthalProjection : MapProjection
|
||||
{
|
||||
public override bool IsNormalCylindrical
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
public override Rect BoundingBoxToRect(BoundingBox boundingBox)
|
||||
{
|
||||
var cbbox = boundingBox as CenteredBoundingBox;
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
#if !WINDOWS_UWP
|
||||
using System.Windows;
|
||||
#endif
|
||||
|
|
@ -16,16 +17,13 @@ namespace MapControl
|
|||
public class EquirectangularProjection : MapProjection
|
||||
{
|
||||
public EquirectangularProjection()
|
||||
: this("EPSG:4326")
|
||||
{
|
||||
CrsId = "EPSG:4326";
|
||||
}
|
||||
|
||||
public EquirectangularProjection(string crsId)
|
||||
public override double TrueScale
|
||||
{
|
||||
CrsId = crsId;
|
||||
HasLatLonBoundingBox = CrsId != "CRS:84";
|
||||
IsNormalCylindrical = true;
|
||||
TrueScale = 1d;
|
||||
get { return 1d; }
|
||||
}
|
||||
|
||||
public override Vector GetMapScale(Location location)
|
||||
|
|
@ -44,5 +42,12 @@ namespace MapControl
|
|||
{
|
||||
return new Location(point.Y, point.X);
|
||||
}
|
||||
|
||||
public override string GetBboxValue(Rect rect)
|
||||
{
|
||||
return string.Format(CultureInfo.InvariantCulture,
|
||||
CrsId != "CRS:84" ? "{1},{0},{3},{2}" : "{0},{1},{2},{3}",
|
||||
rect.X, rect.Y, (rect.X + rect.Width), (rect.Y + rect.Height));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,13 +15,8 @@ namespace MapControl
|
|||
public class GnomonicProjection : AzimuthalProjection
|
||||
{
|
||||
public GnomonicProjection()
|
||||
: this("AUTO2:97001") // GeoServer non-standard CRS ID
|
||||
{
|
||||
}
|
||||
|
||||
public GnomonicProjection(string crsId)
|
||||
{
|
||||
CrsId = crsId;
|
||||
CrsId = "AUTO2:97001"; // GeoServer non-standard CRS ID
|
||||
}
|
||||
|
||||
public override Point LocationToPoint(Location location)
|
||||
|
|
|
|||
|
|
@ -351,7 +351,7 @@ namespace MapControl
|
|||
var rect = MapProjection.BoundingBoxToRect(boundingBox);
|
||||
var center = new Point(rect.X + rect.Width / 2d, rect.Y + rect.Height / 2d);
|
||||
var scale = Math.Min(RenderSize.Width / rect.Width, RenderSize.Height / rect.Height)
|
||||
* MapProjection.TrueScale / MapProjection.PixelPerDegree;
|
||||
* MapProjection.TrueScale * 360d / MapProjection.TileSize;
|
||||
|
||||
TargetZoomLevel = Math.Log(scale, 2d);
|
||||
TargetCenter = MapProjection.PointToLocation(center);
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
#if WINDOWS_UWP
|
||||
using Windows.Foundation;
|
||||
#else
|
||||
|
|
@ -18,7 +19,6 @@ namespace MapControl
|
|||
public abstract class MapProjection
|
||||
{
|
||||
public const int TileSize = 256;
|
||||
public const double PixelPerDegree = TileSize / 360d;
|
||||
|
||||
public const double Wgs84EquatorialRadius = 6378137d;
|
||||
public const double Wgs84Flattening = 1d / 298.257223563;
|
||||
|
|
@ -29,34 +29,40 @@ namespace MapControl
|
|||
/// <summary>
|
||||
/// Gets or sets the WMS 1.3.0 CRS identifier.
|
||||
/// </summary>
|
||||
public string CrsId { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates if a lat/lon coordinate system is used for the WMS BBOX query parameter,
|
||||
/// like e.g. in an EquirectangularProjection with CrsId="EPSG:4326" (but not CrsId="CRS:84").
|
||||
/// </summary>
|
||||
public bool HasLatLonBoundingBox { get; protected set; }
|
||||
public string CrsId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates if this is a normal cylindrical projection.
|
||||
/// </summary>
|
||||
public bool IsNormalCylindrical { get; protected set; }
|
||||
public virtual bool IsNormalCylindrical
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates if this is a web mercator projection, i.e. compatible with MapTileLayer.
|
||||
/// </summary>
|
||||
public bool IsWebMercator { get; protected set; }
|
||||
public virtual bool IsWebMercator
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the absolute value of the minimum and maximum latitude that can be transformed.
|
||||
/// </summary>
|
||||
public virtual double MaxLatitude
|
||||
{
|
||||
get { return 90d; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public double TrueScale { get; protected set; } = Wgs84MetersPerDegree;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the absolute value of the minimum and maximum latitude that can be transformed.
|
||||
/// </summary>
|
||||
public double MaxLatitude { get; protected set; } = 90d;
|
||||
public virtual double TrueScale
|
||||
{
|
||||
get { return Wgs84MetersPerDegree; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the projection center. Only relevant for azimuthal pprojections.
|
||||
|
|
@ -152,13 +158,32 @@ namespace MapControl
|
|||
return RectToBoundingBox(rect);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the CRS parameter value for a WMS GetMap request.
|
||||
/// </summary>
|
||||
public virtual string GetCrsValue()
|
||||
{
|
||||
return CrsId.StartsWith("AUTO:") || CrsId.StartsWith("AUTO2:")
|
||||
? string.Format(CultureInfo.InvariantCulture, "{0},1,{1},{2}", CrsId, ProjectionCenter.Longitude, ProjectionCenter.Latitude)
|
||||
: CrsId;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the BBOX parameter value for a WMS GetMap request.
|
||||
/// </summary>
|
||||
public virtual string GetBboxValue(Rect rect)
|
||||
{
|
||||
return string.Format(CultureInfo.InvariantCulture,
|
||||
"{0},{1},{2},{3}", rect.X, rect.Y, (rect.X + rect.Width), (rect.Y + rect.Height));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets ProjectionCenter, ViewportScale, ViewportTransform and InverseViewportTransform.
|
||||
/// </summary>
|
||||
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) * TileSize / (360d * TrueScale);
|
||||
|
||||
var center = LocationToPoint(mapCenter);
|
||||
var matrix = CreateTransformMatrix(center, ViewportScale, -ViewportScale, heading, viewportCenter);
|
||||
|
|
|
|||
|
|
@ -15,13 +15,8 @@ namespace MapControl
|
|||
public class OrthographicProjection : AzimuthalProjection
|
||||
{
|
||||
public OrthographicProjection()
|
||||
: this("AUTO2:42003")
|
||||
{
|
||||
}
|
||||
|
||||
public OrthographicProjection(string crsId)
|
||||
{
|
||||
CrsId = crsId;
|
||||
CrsId = "AUTO2:42003";
|
||||
}
|
||||
|
||||
public override Point LocationToPoint(Location location)
|
||||
|
|
|
|||
|
|
@ -15,13 +15,8 @@ namespace MapControl
|
|||
public class StereographicProjection : AzimuthalProjection
|
||||
{
|
||||
public StereographicProjection()
|
||||
: this("AUTO2:97002") // GeoServer non-standard CRS ID
|
||||
{
|
||||
}
|
||||
|
||||
public StereographicProjection(string crsId)
|
||||
{
|
||||
CrsId = crsId;
|
||||
CrsId = "AUTO2:97002"; // GeoServer non-standard CRS ID
|
||||
}
|
||||
|
||||
public override Point LocationToPoint(Location location)
|
||||
|
|
|
|||
|
|
@ -15,17 +15,21 @@ namespace MapControl
|
|||
/// </summary>
|
||||
public class WebMercatorProjection : MapProjection
|
||||
{
|
||||
private static readonly double maxLatitude = YToLatitude(180d);
|
||||
|
||||
public WebMercatorProjection()
|
||||
: this("EPSG:3857")
|
||||
{
|
||||
CrsId = "EPSG:3857";
|
||||
}
|
||||
|
||||
public WebMercatorProjection(string crsId)
|
||||
public override bool IsWebMercator
|
||||
{
|
||||
CrsId = crsId;
|
||||
IsNormalCylindrical = true;
|
||||
IsWebMercator = true;
|
||||
MaxLatitude = YToLatitude(180d);
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
public override double MaxLatitude
|
||||
{
|
||||
get { return maxLatitude; }
|
||||
}
|
||||
|
||||
public override Vector GetMapScale(Location location)
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ using System.Collections.Generic;
|
|||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Globalization;
|
||||
using System.Xml.Linq;
|
||||
#if WINDOWS_UWP
|
||||
using Windows.Foundation;
|
||||
|
|
@ -65,17 +64,17 @@ namespace MapControl
|
|||
/// <summary>
|
||||
/// Gets a list of all layer names returned by a GetCapabilities response.
|
||||
/// </summary>
|
||||
public async Task<IList<string>> GetLayerNamesAsync()
|
||||
public async Task<List<string>> GetLayerNamesAsync()
|
||||
{
|
||||
IList<string> layerNames = null;
|
||||
List<string> layerNames = null;
|
||||
|
||||
if (ServiceUri != null)
|
||||
{
|
||||
var capabilitiesUri = GetRequestUri("GetCapabilities").Replace(" ", "%20");
|
||||
var uri = GetRequestUri("GetCapabilities").Replace(" ", "%20");
|
||||
|
||||
try
|
||||
{
|
||||
var stream = await ImageLoader.HttpClient.GetStreamAsync(capabilitiesUri);
|
||||
var stream = await ImageLoader.HttpClient.GetStreamAsync(uri);
|
||||
var capabilities = XDocument.Load(stream).Root;
|
||||
var ns = capabilities.Name.Namespace;
|
||||
|
||||
|
|
@ -88,31 +87,36 @@ namespace MapControl
|
|||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine("WmsImageLayer: {0}: {1}", capabilitiesUri, ex.Message);
|
||||
Debug.WriteLine("WmsImageLayer: {0}: {1}", uri, ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
return layerNames;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calls GetImageUri() and asynchronously loads an ImageSource from the returned GetMap URL.
|
||||
/// </summary>
|
||||
protected override async Task<ImageSource> GetImageAsync()
|
||||
{
|
||||
var uri = GetImageUri();
|
||||
|
||||
return uri != null ? await ImageLoader.LoadImageAsync(uri) : null;
|
||||
return uri != null
|
||||
? await ImageLoader.LoadImageAsync(new Uri(uri.Replace(" ", "%20")))
|
||||
: null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a GetMap request URL for the current BoundingBox.
|
||||
/// Returns a GetMap request URL string.
|
||||
/// </summary>
|
||||
protected virtual Uri GetImageUri()
|
||||
protected virtual string GetImageUri()
|
||||
{
|
||||
Uri imageUri = null;
|
||||
string uri = null;
|
||||
var projection = ParentMap?.MapProjection;
|
||||
|
||||
if (ServiceUri != null && projection != null && !string.IsNullOrEmpty(projection.CrsId))
|
||||
{
|
||||
var uri = GetRequestUri("GetMap");
|
||||
uri = GetRequestUri("GetMap");
|
||||
|
||||
if (uri.IndexOf("LAYERS=", StringComparison.OrdinalIgnoreCase) < 0 && Layers != null)
|
||||
{
|
||||
|
|
@ -131,42 +135,13 @@ namespace MapControl
|
|||
|
||||
var rect = projection.BoundingBoxToRect(BoundingBox);
|
||||
|
||||
uri += "&" + GetCrsParam(projection);
|
||||
uri += "&" + GetBboxParam(projection, rect);
|
||||
uri += "&CRS=" + projection.GetCrsValue();
|
||||
uri += "&BBOX=" + projection.GetBboxValue(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 imageUri;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the effective value of the CRS query parameter.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected virtual string GetCrsParam(MapProjection projection)
|
||||
{
|
||||
var crs = "CRS=" + projection.CrsId;
|
||||
|
||||
if (projection.CrsId.StartsWith("AUTO2:"))
|
||||
{
|
||||
crs += string.Format(CultureInfo.InvariantCulture, ",1,{0},{1}",
|
||||
projection.ProjectionCenter.Longitude, projection.ProjectionCenter.Latitude);
|
||||
}
|
||||
|
||||
return crs;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the effective value of the BBOX (or some equivalent) query parameter.
|
||||
/// </summary>
|
||||
protected virtual string GetBboxParam(MapProjection projection, Rect bbox)
|
||||
{
|
||||
return string.Format(CultureInfo.InvariantCulture,
|
||||
projection.HasLatLonBoundingBox ? "BBOX={1},{0},{3},{2}" : "BBOX={0},{1},{2},{3}",
|
||||
bbox.X, bbox.Y, (bbox.X + bbox.Width), (bbox.Y + bbox.Height));
|
||||
return uri;
|
||||
}
|
||||
|
||||
private string GetRequestUri(string request)
|
||||
|
|
|
|||
|
|
@ -15,19 +15,19 @@ namespace MapControl
|
|||
/// </summary>
|
||||
public class WorldMercatorProjection : MapProjection
|
||||
{
|
||||
private static readonly double maxLatitude = YToLatitude(180d);
|
||||
|
||||
public static double ConvergenceTolerance = 1e-6;
|
||||
public static int MaxIterations = 10;
|
||||
|
||||
public WorldMercatorProjection()
|
||||
: this("EPSG:3395")
|
||||
{
|
||||
CrsId = "EPSG:3395";
|
||||
}
|
||||
|
||||
public WorldMercatorProjection(string crsId)
|
||||
public override double MaxLatitude
|
||||
{
|
||||
CrsId = crsId;
|
||||
IsNormalCylindrical = true;
|
||||
MaxLatitude = YToLatitude(180d);
|
||||
get { return maxLatitude; }
|
||||
}
|
||||
|
||||
public override Vector GetMapScale(Location location)
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
#if !WINDOWS_UWP
|
||||
using System.Windows;
|
||||
#endif
|
||||
|
|
@ -20,6 +21,10 @@ namespace MapControl.Projections
|
|||
public class GeoApiProjection : MapProjection
|
||||
{
|
||||
private ICoordinateSystem coordinateSystem;
|
||||
private bool isNormalCylindrical;
|
||||
private bool isWebMercator;
|
||||
private double trueScale;
|
||||
private string bboxFormat;
|
||||
|
||||
public IMathTransform LocationToPointTransform { get; private set; }
|
||||
public IMathTransform PointToLocationTransform { get; private set; }
|
||||
|
|
@ -63,21 +68,21 @@ namespace MapControl.Projections
|
|||
var falseNorthing = projection.GetParameter("false_northing");
|
||||
var scaleFactor = projection.GetParameter("scale_factor");
|
||||
|
||||
HasLatLonBoundingBox = false;
|
||||
IsNormalCylindrical =
|
||||
isNormalCylindrical =
|
||||
centralMeridian != null && centralMeridian.Value == 0d &&
|
||||
centralParallel != null && centralParallel.Value == 0d &&
|
||||
(falseEasting == null || falseEasting.Value == 0d) &&
|
||||
(falseNorthing == null || falseNorthing.Value == 0d);
|
||||
IsWebMercator = CrsId == "EPSG:3857" || CrsId == "EPSG:900913";
|
||||
TrueScale = (scaleFactor != null ? scaleFactor.Value : 1d) * Wgs84MetersPerDegree;
|
||||
isWebMercator = CrsId == "EPSG:3857" || CrsId == "EPSG:900913";
|
||||
trueScale = (scaleFactor != null ? scaleFactor.Value : 1d) * Wgs84MetersPerDegree;
|
||||
bboxFormat = "{0},{1},{2},{3}";
|
||||
}
|
||||
else
|
||||
{
|
||||
HasLatLonBoundingBox = true;
|
||||
IsNormalCylindrical = true;
|
||||
IsWebMercator = false;
|
||||
TrueScale = 1d;
|
||||
isNormalCylindrical = true;
|
||||
isWebMercator = false;
|
||||
trueScale = 1d;
|
||||
bboxFormat = "{1},{0},{3},{2}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -93,6 +98,21 @@ namespace MapControl.Projections
|
|||
set { CoordinateSystem = new CoordinateSystemFactory().CreateFromWkt(value); }
|
||||
}
|
||||
|
||||
public override bool IsNormalCylindrical
|
||||
{
|
||||
get { return isNormalCylindrical; }
|
||||
}
|
||||
|
||||
public override bool IsWebMercator
|
||||
{
|
||||
get { return isWebMercator; }
|
||||
}
|
||||
|
||||
public override double TrueScale
|
||||
{
|
||||
get { return trueScale; }
|
||||
}
|
||||
|
||||
public override Point LocationToPoint(Location location)
|
||||
{
|
||||
if (LocationToPointTransform == null)
|
||||
|
|
@ -116,5 +136,11 @@ namespace MapControl.Projections
|
|||
|
||||
return new Location(coordinate.Y, coordinate.X);
|
||||
}
|
||||
|
||||
public override string GetBboxValue(Rect rect)
|
||||
{
|
||||
return string.Format(CultureInfo.InvariantCulture,
|
||||
bboxFormat, rect.X, rect.Y, (rect.X + rect.Width), (rect.Y + rect.Height));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,13 +27,17 @@ namespace MapControl.Projections
|
|||
public PolarStereographicProjection(string crsId, bool north, double scaleFactor = 1d, double falseEasting = 0d, double falseNorthing = 0d)
|
||||
{
|
||||
CrsId = crsId;
|
||||
TrueScale = scaleFactor * Wgs84MetersPerDegree;
|
||||
this.north = north;
|
||||
this.scaleFactor = scaleFactor;
|
||||
this.falseEasting = falseEasting;
|
||||
this.falseNorthing = falseNorthing;
|
||||
}
|
||||
|
||||
public override double TrueScale
|
||||
{
|
||||
get { return scaleFactor * Wgs84MetersPerDegree; }
|
||||
}
|
||||
|
||||
public override Vector GetMapScale(Location location)
|
||||
{
|
||||
var lat = (north ? location.Latitude : -location.Latitude) * Math.PI / 180d;
|
||||
|
|
|
|||
Loading…
Reference in a new issue