diff --git a/MapControl/Shared/AutoEquirectangularProjection.cs b/MapControl/Shared/AutoEquirectangularProjection.cs
index dbdb00c8..af022da4 100644
--- a/MapControl/Shared/AutoEquirectangularProjection.cs
+++ b/MapControl/Shared/AutoEquirectangularProjection.cs
@@ -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)
diff --git a/MapControl/Shared/AzimuthalEquidistantProjection.cs b/MapControl/Shared/AzimuthalEquidistantProjection.cs
index ec093b03..3630cecf 100644
--- a/MapControl/Shared/AzimuthalEquidistantProjection.cs
+++ b/MapControl/Shared/AzimuthalEquidistantProjection.cs
@@ -14,15 +14,7 @@ namespace MapControl
///
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)
{
diff --git a/MapControl/Shared/AzimuthalProjection.cs b/MapControl/Shared/AzimuthalProjection.cs
index 7af050f0..79728336 100644
--- a/MapControl/Shared/AzimuthalProjection.cs
+++ b/MapControl/Shared/AzimuthalProjection.cs
@@ -16,6 +16,11 @@ namespace MapControl
///
public abstract class AzimuthalProjection : MapProjection
{
+ public override bool IsNormalCylindrical
+ {
+ get { return false; }
+ }
+
public override Rect BoundingBoxToRect(BoundingBox boundingBox)
{
var cbbox = boundingBox as CenteredBoundingBox;
diff --git a/MapControl/Shared/EquirectangularProjection.cs b/MapControl/Shared/EquirectangularProjection.cs
index c7e0c955..7e85f1b2 100644
--- a/MapControl/Shared/EquirectangularProjection.cs
+++ b/MapControl/Shared/EquirectangularProjection.cs
@@ -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));
+ }
}
}
diff --git a/MapControl/Shared/GnomonicProjection.cs b/MapControl/Shared/GnomonicProjection.cs
index 2570aab6..26ea93cf 100644
--- a/MapControl/Shared/GnomonicProjection.cs
+++ b/MapControl/Shared/GnomonicProjection.cs
@@ -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)
diff --git a/MapControl/Shared/MapBase.cs b/MapControl/Shared/MapBase.cs
index ebd4ad70..6a1abc0c 100644
--- a/MapControl/Shared/MapBase.cs
+++ b/MapControl/Shared/MapBase.cs
@@ -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);
diff --git a/MapControl/Shared/MapProjection.cs b/MapControl/Shared/MapProjection.cs
index 721b6889..a2c091ab 100644
--- a/MapControl/Shared/MapProjection.cs
+++ b/MapControl/Shared/MapProjection.cs
@@ -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
///
/// Gets or sets the WMS 1.3.0 CRS identifier.
///
- public string CrsId { get; protected set; }
-
- ///
- /// 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").
- ///
- public bool HasLatLonBoundingBox { get; protected set; }
+ public string CrsId { get; set; }
///
/// Indicates if this is a normal cylindrical projection.
///
- public bool IsNormalCylindrical { get; protected set; }
+ public virtual bool IsNormalCylindrical
+ {
+ get { return true; }
+ }
///
/// Indicates if this is a web mercator projection, i.e. compatible with MapTileLayer.
///
- public bool IsWebMercator { get; protected set; }
+ public virtual bool IsWebMercator
+ {
+ get { return false; }
+ }
+
+ ///
+ /// Gets the absolute value of the minimum and maximum latitude that can be transformed.
+ ///
+ public virtual double MaxLatitude
+ {
+ get { return 90d; }
+ }
///
/// 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.
///
- public double TrueScale { get; protected set; } = Wgs84MetersPerDegree;
-
- ///
- /// Gets the absolute value of the minimum and maximum latitude that can be transformed.
- ///
- public double MaxLatitude { get; protected set; } = 90d;
+ public virtual double TrueScale
+ {
+ get { return Wgs84MetersPerDegree; }
+ }
///
/// Gets the projection center. Only relevant for azimuthal pprojections.
@@ -152,13 +158,32 @@ namespace MapControl
return RectToBoundingBox(rect);
}
+ ///
+ /// Gets the CRS parameter value for a WMS GetMap request.
+ ///
+ 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;
+ }
+
+ ///
+ /// Gets the BBOX parameter value for a WMS GetMap request.
+ ///
+ 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));
+ }
+
///
/// Sets ProjectionCenter, ViewportScale, ViewportTransform and InverseViewportTransform.
///
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);
diff --git a/MapControl/Shared/OrthographicProjection.cs b/MapControl/Shared/OrthographicProjection.cs
index cbcaf7a3..acea7799 100644
--- a/MapControl/Shared/OrthographicProjection.cs
+++ b/MapControl/Shared/OrthographicProjection.cs
@@ -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)
diff --git a/MapControl/Shared/StereographicProjection.cs b/MapControl/Shared/StereographicProjection.cs
index d27f9d80..cd9c7171 100644
--- a/MapControl/Shared/StereographicProjection.cs
+++ b/MapControl/Shared/StereographicProjection.cs
@@ -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)
diff --git a/MapControl/Shared/WebMercatorProjection.cs b/MapControl/Shared/WebMercatorProjection.cs
index e7972fae..1ce60b6a 100644
--- a/MapControl/Shared/WebMercatorProjection.cs
+++ b/MapControl/Shared/WebMercatorProjection.cs
@@ -15,17 +15,21 @@ namespace MapControl
///
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)
diff --git a/MapControl/Shared/WmsImageLayer.cs b/MapControl/Shared/WmsImageLayer.cs
index 9eb86381..59e5858a 100644
--- a/MapControl/Shared/WmsImageLayer.cs
+++ b/MapControl/Shared/WmsImageLayer.cs
@@ -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
///
/// Gets a list of all layer names returned by a GetCapabilities response.
///
- public async Task> GetLayerNamesAsync()
+ public async Task> GetLayerNamesAsync()
{
- IList layerNames = null;
+ List 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;
}
+ ///
+ /// Calls GetImageUri() and asynchronously loads an ImageSource from the returned GetMap URL.
+ ///
protected override async Task GetImageAsync()
{
var uri = GetImageUri();
- return uri != null ? await ImageLoader.LoadImageAsync(uri) : null;
+ return uri != null
+ ? await ImageLoader.LoadImageAsync(new Uri(uri.Replace(" ", "%20")))
+ : null;
}
///
- /// Returns a GetMap request URL for the current BoundingBox.
+ /// Returns a GetMap request URL string.
///
- 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;
- }
-
- ///
- /// Gets the effective value of the CRS query parameter.
- ///
- ///
- 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;
- }
-
- ///
- /// Gets the effective value of the BBOX (or some equivalent) query parameter.
- ///
- 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)
diff --git a/MapControl/Shared/WorldMercatorProjection.cs b/MapControl/Shared/WorldMercatorProjection.cs
index 74e08ffb..5801b66d 100644
--- a/MapControl/Shared/WorldMercatorProjection.cs
+++ b/MapControl/Shared/WorldMercatorProjection.cs
@@ -15,19 +15,19 @@ namespace MapControl
///
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)
diff --git a/MapProjections/Shared/GeoApiProjection.cs b/MapProjections/Shared/GeoApiProjection.cs
index c7332df5..3d4bd867 100644
--- a/MapProjections/Shared/GeoApiProjection.cs
+++ b/MapProjections/Shared/GeoApiProjection.cs
@@ -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));
+ }
}
}
diff --git a/MapProjections/Shared/PolarStereographicProjection.cs b/MapProjections/Shared/PolarStereographicProjection.cs
index fb2d04e8..0b46a19a 100644
--- a/MapProjections/Shared/PolarStereographicProjection.cs
+++ b/MapProjections/Shared/PolarStereographicProjection.cs
@@ -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;