// 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);
}
}
}