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