// Copyright (C) Stichting Deltares 2019. All rights reserved. // // This file is part of Riskeer. // // Riskeer is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License // along with this program. If not, see . // // All names, logos, and references to "Deltares" are registered trademarks of // Stichting Deltares and remain full property of Stichting Deltares at all times. // All rights reserved. using System; using System.Collections.Generic; using System.Linq; using DotSpatial.Data; using DotSpatial.Projections; using GeoAPI.Geometries; using DotSpatialReproject = DotSpatial.Projections.Reproject; namespace Core.Components.DotSpatial.Projections { /// /// Extension methods for reprojecting objects from one coordinate system to another. /// /// /// Original source: https://github.com/FObermaier/DotSpatial.Plugins/blob/master/DotSpatial.Plugins.BruTileLayer/Reprojection/ReprojectExtensions.cs /// Original license: http://www.apache.org/licenses/LICENSE-2.0.html /// internal static class ReprojectExtensions { /// /// Reprojects a . /// /// The object to be reprojected. /// The coordinate system corresponding to . /// The target coordinate system. /// The reprojected . /// The returned object is specified in a higher resolution than . /// This is done as a straight edge in can lead to a curved /// edge in . /// Thrown when any input argument is null. /// Thrown when has less /// than 3 coordinates. public static ILinearRing Reproject(this ILinearRing ring, ProjectionInfo source, ProjectionInfo target) { if (ring == null) { throw new ArgumentNullException(nameof(ring)); } if (source == null) { throw new ArgumentNullException(nameof(source)); } if (target == null) { throw new ArgumentNullException(nameof(target)); } Coordinate[] seq = Reproject(ring.Coordinates.Densify(36).ToArray(), source, target); return ring.Factory.CreateLinearRing(seq); } /// /// Reprojects a . /// /// The object to be reprojected. /// The coordinate system corresponding to . /// The target coordinate system. /// The reprojected . /// Thrown when any input argument is null. public static Extent Reproject(this Extent extent, ProjectionInfo source, ProjectionInfo target) { if (extent == null) { throw new ArgumentNullException(nameof(extent)); } if (source == null) { throw new ArgumentNullException(nameof(source)); } if (target == null) { throw new ArgumentNullException(nameof(target)); } if (target.Transform == null) { return extent; } double[] xy = ToSequence(extent); DotSpatialReproject.ReprojectPoints(xy, null, source, target, 0, xy.Length / 2); return ToExtent(xy); } private static Coordinate[] Reproject(this Coordinate[] coordinates, ProjectionInfo source, ProjectionInfo target) { if (target.Transform == null) { return coordinates; } var xy = new double[coordinates.Length * 2]; var z = new double[coordinates.Length]; var j = 0; for (var i = 0; i < coordinates.Length; i++) { Coordinate c = coordinates[i]; xy[j] = c.X; xy[j + 1] = c.Y; j = j + 2; z[i] = double.IsNaN(c.Z) ? 0 : c.Z; } DotSpatialReproject.ReprojectPoints(xy, z, source, target, 0, coordinates.Length); var result = new List(coordinates.Length); j = 0; for (var i = 0; i < coordinates.Length; i++) { result.Add(new Coordinate(xy[j++], xy[j++])); } return result.ToArray(); } /// /// Returns a collection of that has the same shape as /// the target, but with more points set 'edge' (effectively increasing its resolution). /// /// The original collection of . /// The number of segments each 'edge' should be subdivided in. /// Return the densified version based on . /// A value of 4 of means that 3 equally spaced /// points are introduced along the line between two instances /// in . private static Coordinate[] Densify(this IList original, int intervals) { return GetDensifiedCoordinates(original, intervals - 1); } private static Coordinate[] GetDensifiedCoordinates(IList original, int numberOfAdditionalPoints) { int numberOfEdges = original.Count - 1; var resultList = new List(numberOfEdges * (numberOfAdditionalPoints + 1) + 1) { original[0] }; for (var i = 1; i <= numberOfEdges; i++) { resultList.AddRange(GetEdgePointsExcludingStart(original[i - 1], original[i], numberOfAdditionalPoints)); } return resultList.ToArray(); } private static IEnumerable GetEdgePointsExcludingStart(Coordinate start, Coordinate end, int numberOfAdditionalPoints) { if (numberOfAdditionalPoints < 0) { throw new ArgumentOutOfRangeException(nameof(numberOfAdditionalPoints), @"Number of additional points cannot be negative."); } return GetEdgePoints(start, end, numberOfAdditionalPoints); } private static IEnumerable GetEdgePoints(Coordinate start, Coordinate end, int numberOfAdditionalPoints) { double dx = (end.X - start.X) / (numberOfAdditionalPoints + 1); double dy = (end.Y - start.Y) / (numberOfAdditionalPoints + 1); for (var i = 1; i <= numberOfAdditionalPoints; i++) { yield return new Coordinate(start.X + i * dx, start.Y + i * dy); } yield return end; } private static double[] ToSequence(Extent extent) { const int horizontalResolution = 72; const int verticalResolution = 36; var xy = new double[horizontalResolution * verticalResolution * 2]; double dx = extent.Width / (horizontalResolution - 1); double dy = extent.Height / (verticalResolution - 1); // Define a lattice of points, because there exist coordinate transformations // that place original center-points to edge-point in the target coordinate system: double y = extent.MinY; var k = 0; for (var i = 0; i < verticalResolution; i++) { double x = extent.MinX; for (var j = 0; j < horizontalResolution; j++) { xy[k] = x; xy[k + 1] = y; k = k + 2; x += dx; } y += dy; } return xy; } private static Extent ToExtent(double[] xyOrdinates) { double minX = double.MaxValue, maxX = double.MinValue; double minY = double.MaxValue, maxY = double.MinValue; var i = 0; while (i < xyOrdinates.Length) { double x = xyOrdinates[i]; if (!double.IsNaN(x) && double.MinValue < x && x < double.MaxValue) { minX = Math.Min(minX, x); maxX = Math.Max(maxX, x); } i += 1; double y = xyOrdinates[i]; if (!double.IsNaN(y) && double.MinValue < y && y < double.MaxValue) { minY = Math.Min(minY, y); maxY = Math.Max(maxY, y); } i += 1; } return new Extent(minX, minY, maxX, maxY); } } }