// Copyright (C) Stichting Deltares 2018. All rights reserved.
//
// This file is part of the Dam Engine.
//
// The Dam Engine is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero 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 Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero 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;
namespace Deltares.DamEngine.Data.Geometry
{
///
/// Exception class for .
///
public class CoordinateSystemConverterException : Exception
{
///
/// Initializes a new instance of the class.
///
/// The message that describes the error.
public CoordinateSystemConverterException(string message)
: base(message) {}
}
///
/// class for converting object coordinates from global to local system and back.
///
/// Converter assumes all points of the given line are in the same vertical
/// 2D plane.
public class CoordinateSystemConverter
{
private double cosAlpha = 0;
private double endX;
private double endY;
private bool isGlobalXYZDefined = false;
private double length;
private double sinAlpha = 0;
private double startX = 0.0;
private double startY = 0.0;
///
/// cos(alpha) of x-axis of local XYZ system
///
public double CosAlpha
{
get
{
ThrowIfGlobalXYZNotDefined();
return cosAlpha;
}
}
///
/// sin(alpha) of x-axis of local XYZ system
///
public double SinAlpha
{
get
{
ThrowIfGlobalXYZNotDefined();
return sinAlpha;
}
}
///
/// x-coordinate of origin of local XYZ system
///
public double XStart
{
get
{
ThrowIfGlobalXYZNotDefined();
return startX;
}
}
///
/// y-coordinate of origin start point of local XYZ system
///
public double YStart
{
get
{
ThrowIfGlobalXYZNotDefined();
return startY;
}
}
///
/// x-coordinate of origin last point
///
public double XEnd
{
get
{
ThrowIfGlobalXYZNotDefined();
return endX;
}
}
///
/// y-coordinate of origin end point
///
public double YEnd
{
get
{
ThrowIfGlobalXYZNotDefined();
return endY;
}
}
///
/// Gets the length.
///
///
/// The length.
///
public double Length
{
get
{
ThrowIfGlobalXYZNotDefined();
return length;
}
}
///
/// Gets a value indicating whether [converted to local].
///
///
/// true if [converted to local]; otherwise, false.
///
public bool ConvertedToLocal { get; private set; }
///
/// Define parameters to parameterize the global reference system.
///
/// Line defined in global coordinates.
public void DefineGlobalXYZBasedOnLine(GeometryPointString globalLine)
{
GeometryPoint firstPoint = globalLine.Points[0];
GeometryPoint lastPoint = globalLine.Points[globalLine.Points.Count - 1];
double dX = lastPoint.X - firstPoint.X;
double dY = lastPoint.Y - firstPoint.Y;
length = Math.Sqrt(dX*dX + dY*dY);
cosAlpha = dX/length;
sinAlpha = dY/length;
startX = firstPoint.X;
startY = firstPoint.Y;
endX = lastPoint.X;
endY = lastPoint.Y;
isGlobalXYZDefined = true;
}
///
/// Define parameters to define global reference system
///
[Obsolete]
public void DefineLocalXZBasedOnSavedProperties(double originalX, double originalY, double angle)
{
cosAlpha = Math.Cos(angle);
sinAlpha = Math.Sin(angle);
startX = originalX;
startY = originalY;
isGlobalXYZDefined = true;
}
///
/// Projects a global coordinate to a local XZ reference system.
///
/// Global coordinate.
///
/// When global parameters haven't
/// been defined yet with .
public void ConvertGlobalXYZToLocalXZ(GeometryPoint globalPoint)
{
ThrowIfGlobalXYZNotDefined();
globalPoint.X = GetLocalXForGlobalPoint(globalPoint);
globalPoint.Y = 0.0;
}
///
/// Projects a line of global coordinates to a local XZ reference system.
///
/// Th line in global coordinates.
///
public void ConvertGlobalXYZToLocalXZ(GeometryPointString globalLine)
{
if (!isGlobalXYZDefined)
{
DefineGlobalXYZBasedOnLine(globalLine);
}
ThrowIfGlobalXYZNotDefined();
foreach (var point in globalLine.Points)
{
ConvertGlobalXYZToLocalXZ(point);
}
ConvertedToLocal = true;
}
///
/// Projects a local coordinate back to the parameterized global XYZ reference system.
///
/// Local coordinate.
///
/// When global parameters haven't
/// been defined yet with .
public void ConvertLocalXZToGlobalXYZ(GeometryPoint localPoint)
{
ThrowIfGlobalXYZNotDefined();
double localX = localPoint.X*cosAlpha + startX;
double localY = localPoint.X*sinAlpha + startY;
localPoint.X = localX;
localPoint.Y = localY;
}
///
/// Projects a XZ-line local coordinates back to the parameterized global XYZ reference system.
///
/// Line in local coordinates.
///
/// When global parameters haven't
/// been defined yet with .
public void ConvertLocalXZToGlobalXYZ(GeometryPointString localLine)
{
ThrowIfGlobalXYZNotDefined();
foreach (var point in localLine.Points)
{
ConvertLocalXZToGlobalXYZ(point);
}
}
private double GetLocalXForGlobalPoint(GeometryPoint globalPoint)
{
const double cSinCosThreshold = 0.4;
double dX = globalPoint.X - startX;
double dY = globalPoint.Y - startY;
double localXfromGlobalX = dX/cosAlpha;
if (Math.Abs(sinAlpha) < cSinCosThreshold)
{
// if line tends to be horizontal use local x calculated based on dX
return localXfromGlobalX;
}
double localXfromGlobalY = dY/sinAlpha;
if (Math.Abs(cosAlpha) < cSinCosThreshold)
{
// if line tends to be vertical use local x calculated based on dY
return localXfromGlobalY;
}
// if angle around 45 degrees then use the mean between the 2 values
return (localXfromGlobalX + localXfromGlobalY)/2;
}
///
/// Raise exception if Global XYZ reference system is not defined
///
private void ThrowIfGlobalXYZNotDefined()
{
if (!isGlobalXYZDefined)
{
throw new CoordinateSystemConverterException("Global reference coordinate system is not defined");
}
}
}
}