From 716cf950f3ca1e5a915e3f9fac2e1ab440d2e6b7 Mon Sep 17 00:00:00 2001 From: ClemensF Date: Fri, 25 Oct 2019 19:56:23 +0200 Subject: [PATCH] Improved MapProjections --- MapControl/Shared/MapProjection.cs | 13 +++- MapControl/UWP/Matrix.UWP.cs | 3 + MapControl/UWP/Point.UWP.cs | 3 + MapProjections/Shared/GeoApiProjection.cs | 62 ++++++++++--------- .../Shared/WebMercatorProjection.cs | 2 - .../Shared/WorldMercatorProjection.cs | 35 ++++++----- 6 files changed, 66 insertions(+), 52 deletions(-) diff --git a/MapControl/Shared/MapProjection.cs b/MapControl/Shared/MapProjection.cs index a3b9cda3..140a44ae 100644 --- a/MapControl/Shared/MapProjection.cs +++ b/MapControl/Shared/MapProjection.cs @@ -5,7 +5,6 @@ using System; #if WINDOWS_UWP using Windows.Foundation; -using Windows.UI.Xaml.Media; #else using System.Windows; using System.Windows.Media; @@ -134,9 +133,17 @@ namespace MapControl /// public BoundingBox ViewportRectToBoundingBox(Rect rect) { - var transform = new MatrixTransform { Matrix = InverseViewportTransform }; + var p1 = InverseViewportTransform.Transform(new Point(rect.X, rect.Y)); + var p2 = InverseViewportTransform.Transform(new Point(rect.X, rect.Y + rect.Height)); + var p3 = InverseViewportTransform.Transform(new Point(rect.X + rect.Width, rect.Y)); + var p4 = InverseViewportTransform.Transform(new Point(rect.X + rect.Width, rect.Y + rect.Height)); - return RectToBoundingBox(transform.TransformBounds(rect)); + rect.X = Math.Min(p1.X, Math.Min(p2.X, Math.Min(p3.X, p4.X))); + rect.Y = Math.Min(p1.Y, Math.Min(p2.Y, Math.Min(p3.Y, p4.Y))); + rect.Width = Math.Max(p1.X, Math.Max(p2.X, Math.Max(p3.X, p4.X))) - rect.X; + rect.Height = Math.Max(p1.Y, Math.Max(p2.Y, Math.Max(p3.Y, p4.Y))) - rect.Y; + + return RectToBoundingBox(rect); } /// diff --git a/MapControl/UWP/Matrix.UWP.cs b/MapControl/UWP/Matrix.UWP.cs index 6d40be4e..e7c0f685 100644 --- a/MapControl/UWP/Matrix.UWP.cs +++ b/MapControl/UWP/Matrix.UWP.cs @@ -6,6 +6,9 @@ using System; namespace MapControl { + /// + /// Replaces Windows.UI.Xaml.Media.Matrix to achieve necessary floating point precision. + /// public struct Matrix { public double M11 { get; set; } diff --git a/MapControl/UWP/Point.UWP.cs b/MapControl/UWP/Point.UWP.cs index 46fa26bf..724a209b 100644 --- a/MapControl/UWP/Point.UWP.cs +++ b/MapControl/UWP/Point.UWP.cs @@ -4,6 +4,9 @@ namespace MapControl { + /// + /// Replaces Windows.Foundation.Point to achieve necessary floating point precision. + /// public struct Point { public double X { get; set; } diff --git a/MapProjections/Shared/GeoApiProjection.cs b/MapProjections/Shared/GeoApiProjection.cs index 3ff14e1e..6db99b4c 100644 --- a/MapProjections/Shared/GeoApiProjection.cs +++ b/MapProjections/Shared/GeoApiProjection.cs @@ -19,15 +19,15 @@ namespace MapControl.Projections /// public class GeoApiProjection : MapProjection { - private IProjectedCoordinateSystem coordinateSystem; + private ICoordinateSystem coordinateSystem; - public IMathTransform MathTransform { get; private set; } - public IMathTransform InverseTransform { get; private set; } + public IMathTransform LocationToPointTransform { get; private set; } + public IMathTransform PointToLocationTransform { get; private set; } /// /// Gets or sets the IProjectedCoordinateSystem of the MapProjection. /// - public IProjectedCoordinateSystem CoordinateSystem + public ICoordinateSystem CoordinateSystem { get { return coordinateSystem; } set @@ -39,43 +39,45 @@ namespace MapControl.Projections coordinateSystem = value; - var coordinateTransform = new CoordinateTransformationFactory() - .CreateFromCoordinateSystems(GeographicCoordinateSystem.WGS84, coordinateSystem); + var transformFactory = new CoordinateTransformationFactory(); - MathTransform = coordinateTransform.MathTransform; - InverseTransform = MathTransform.Inverse(); + LocationToPointTransform = transformFactory + .CreateFromCoordinateSystems(GeographicCoordinateSystem.WGS84, coordinateSystem) + .MathTransform; + + PointToLocationTransform = transformFactory + .CreateFromCoordinateSystems(coordinateSystem, GeographicCoordinateSystem.WGS84) + .MathTransform; CrsId = (!string.IsNullOrEmpty(coordinateSystem.Authority) && coordinateSystem.AuthorityCode > 0) ? string.Format("{0}:{1}", coordinateSystem.Authority, coordinateSystem.AuthorityCode) : null; - if (!IsWebMercator) - { - IsWebMercator = CrsId == "EPSG:3857" || CrsId == "EPSG:900913"; - } + IsWebMercator = CrsId == "EPSG:3857" || CrsId == "EPSG:900913"; - var projection = coordinateSystem.Projection; - var scaleFactor = projection.GetParameter("scale_factor"); + var projectedCoordinateSystem = coordinateSystem as IProjectedCoordinateSystem; - if (scaleFactor != null) - { - TrueScale = scaleFactor.Value * Wgs84MetersPerDegree; - } - - if (!IsNormalCylindrical) + if (projectedCoordinateSystem != null) { + var projection = projectedCoordinateSystem.Projection; var centralMeridian = projection.GetParameter("central_meridian") ?? projection.GetParameter("longitude_of_origin"); var centralParallel = projection.GetParameter("latitude_of_origin") ?? projection.GetParameter("central_parallel"); var falseEasting = projection.GetParameter("false_easting"); var falseNorthing = projection.GetParameter("false_northing"); + var scaleFactor = projection.GetParameter("scale_factor"); - if (centralMeridian != null && centralMeridian.Value == 0d && + IsNormalCylindrical = + centralMeridian != null && centralMeridian.Value == 0d && centralParallel != null && centralParallel.Value == 0d && (falseEasting == null || falseEasting.Value == 0d) && - (falseNorthing == null || falseNorthing.Value == 0d)) - { - IsNormalCylindrical = true; - } + (falseNorthing == null || falseNorthing.Value == 0d); + + TrueScale = (scaleFactor != null ? scaleFactor.Value : 1d) * Wgs84MetersPerDegree; + } + else + { + IsNormalCylindrical = true; + TrueScale = 1d; } } } @@ -88,29 +90,29 @@ namespace MapControl.Projections public string WKT { get { return CoordinateSystem?.WKT; } - set { CoordinateSystem = (IProjectedCoordinateSystem)new CoordinateSystemFactory().CreateFromWkt(value); } + set { CoordinateSystem = new CoordinateSystemFactory().CreateFromWkt(value); } } public override Point LocationToPoint(Location location) { - if (MathTransform == null) + if (LocationToPointTransform == null) { throw new InvalidOperationException("The CoordinateSystem property is not set."); } - var coordinate = MathTransform.Transform(new Coordinate(location.Longitude, location.Latitude)); + var coordinate = LocationToPointTransform.Transform(new Coordinate(location.Longitude, location.Latitude)); return new Point(coordinate.X, coordinate.Y); } public override Location PointToLocation(Point point) { - if (InverseTransform == null) + if (PointToLocationTransform == null) { throw new InvalidOperationException("The CoordinateSystem property is not set."); } - var coordinate = InverseTransform.Transform(new Coordinate(point.X, point.Y)); + var coordinate = PointToLocationTransform.Transform(new Coordinate(point.X, point.Y)); return new Location(coordinate.Y, coordinate.X); } diff --git a/MapProjections/Shared/WebMercatorProjection.cs b/MapProjections/Shared/WebMercatorProjection.cs index 1b5a7f07..04d9bf6c 100644 --- a/MapProjections/Shared/WebMercatorProjection.cs +++ b/MapProjections/Shared/WebMercatorProjection.cs @@ -18,8 +18,6 @@ namespace MapControl.Projections { public WebMercatorProjection() { - IsWebMercator = true; - IsNormalCylindrical = true; CoordinateSystem = ProjectedCoordinateSystem.WebMercator; } diff --git a/MapProjections/Shared/WorldMercatorProjection.cs b/MapProjections/Shared/WorldMercatorProjection.cs index 2ad26bde..c30cadfe 100644 --- a/MapProjections/Shared/WorldMercatorProjection.cs +++ b/MapProjections/Shared/WorldMercatorProjection.cs @@ -17,23 +17,24 @@ namespace MapControl.Projections { public WorldMercatorProjection() { - IsNormalCylindrical = true; - WKT = "PROJCS[\"WGS 84 / World Mercator\"," - + "GEOGCS[\"WGS 84\"," - + "DATUM[\"WGS_1984\", SPHEROID[\"WGS 84\", 6378137, 298.257223563, AUTHORITY[\"EPSG\", \"7030\"]], AUTHORITY[\"EPSG\", \"6326\"]]," - + "PRIMEM[\"Greenwich\", 0, AUTHORITY[\"EPSG\", \"8901\"]]," - + "UNIT[\"degree\", 0.0174532925199433, AUTHORITY[\"EPSG\", \"9122\"]]," - + "AUTHORITY[\"EPSG\", \"4326\"]]," - + "PROJECTION[\"Mercator_1SP\"]," - + "PARAMETER[\"latitude_of_origin\", 0]," - + "PARAMETER[\"central_meridian\", 0]," - + "PARAMETER[\"scale_factor\", 1]," - + "PARAMETER[\"false_easting\", 0]," - + "PARAMETER[\"false_northing\", 0]," - + "UNIT[\"metre\", 1, AUTHORITY[\"EPSG\", \"9001\"]]," - + "AXIS[\"Easting\", EAST]," - + "AXIS[\"Northing\", NORTH]," - + "AUTHORITY[\"EPSG\", \"3395\"]]"; + WKT = "PROJCS[\"WGS 84 / World Mercator\"," + + "GEOGCS[\"WGS 84\"," + + "DATUM[\"WGS_1984\"," + + "SPHEROID[\"WGS 84\",6378137,298.257223563,AUTHORITY[\"EPSG\",\"7030\"]]," + + "AUTHORITY[\"EPSG\",\"6326\"]]," + + "PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]]," + + "UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]]," + + "AUTHORITY[\"EPSG\",\"4326\"]]," + + "PROJECTION[\"Mercator_1SP\"]," + + "PARAMETER[\"latitude_of_origin\",0]," + + "PARAMETER[\"central_meridian\",0]," + + "PARAMETER[\"scale_factor\",1]," + + "PARAMETER[\"false_easting\",0]," + + "PARAMETER[\"false_northing\",0]," + + "UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]]," + + "AXIS[\"Easting\",EAST]," + + "AXIS[\"Northing\",NORTH]," + + "AUTHORITY[\"EPSG\",\"3395\"]]"; } public override Vector GetMapScale(Location location)