View Javadoc
1   package org.opentrafficsim.core.math;
2   
3   import java.util.Locale;
4   
5   import org.djunits.unit.DirectionUnit;
6   import org.djunits.unit.SpeedUnit;
7   import org.djunits.value.ValueRuntimeException;
8   import org.djunits.value.vdouble.scalar.Direction;
9   import org.djunits.value.vdouble.scalar.Speed;
10  import org.djunits.value.vdouble.vector.SpeedVector;
11  import org.opentrafficsim.base.OtsRuntimeException;
12  
13  /**
14   * A 3D speed vector, decomposed in X, Y, and Z-speed with easy conversion from and to a spherical coordinate system. <br>
15   * <a href="https://en.wikipedia.org/wiki/Spherical_coordinate_system">Physicists and mathematicians <strong>do not</strong>
16   * agree on the meaning of theta and phi.</a> In this class the convention in the physics domain is used:
17   * <ul>
18   * <li>theta is the angle from the z direction.</li>
19   * <li>phi is the projected angle in the xy-plane from the x direction.</li>
20   * </ul>
21   * N.B. In the geography domain yet another convention is used. <br>
22   * <p>
23   * Copyright (c) 2013-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
24   * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
25   * </p>
26   * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
27   * @author <a href="https://github.com/peter-knoppers">Peter Knoppers</a>
28   */
29  public class Speed3d
30  {
31      /** The speed in 3D (XYZ coded). */
32      private final SpeedVector speed;
33  
34      /**
35       * Construct a new Speed3d from vector of strongly typed Cartesian coordinates.
36       * @param speed the speeds in 3D (YPR coded)
37       * @throws ValueRuntimeException in case the vector does not have exactly three elements
38       */
39      public Speed3d(final SpeedVector speed) throws ValueRuntimeException
40      {
41          if (speed.size() != 3)
42          {
43              throw new ValueRuntimeException("Size of an RPY-speed vector should be exactly 3. Got: " + speed);
44          }
45          this.speed = speed;
46      }
47  
48      /**
49       * Construct a new Speed3d from three strongly typed Cartesian coordinates.
50       * @param x the speed in the x-direction
51       * @param y the speed in the y-direction
52       * @param z the speed in the z-direction
53       * @throws ValueRuntimeException in case the units are incorrect
54       */
55      public Speed3d(final Speed x, final Speed y, final Speed z) throws ValueRuntimeException
56      {
57          this.speed = new SpeedVector(new Speed[] {x, y, z}, x.getDisplayUnit());
58      }
59  
60      /**
61       * Construct a new Speed3d from three double Cartesian coordinates and a speed unit.
62       * @param x the speed in the x-direction
63       * @param y the speed in the y-direction
64       * @param z the speed in the z-direction
65       * @param unit the unit of the xyz parameters
66       * @throws ValueRuntimeException in case the units are incorrect
67       */
68      public Speed3d(final double x, final double y, final double z, final SpeedUnit unit) throws ValueRuntimeException
69      {
70          this.speed = new SpeedVector(new double[] {x, y, z}, unit);
71      }
72  
73      /**
74       * Construct a new Speed3d from a strongly typed speed and polar coordinates.
75       * @param speed the speed in the direction of the angle along the vector
76       * @param theta the angle from the z direction
77       * @param phi the projected angle in the xy-plane from the x direction
78       * @throws ValueRuntimeException in case the vector does not have exactly three elements
79       */
80      public Speed3d(final Speed speed, final Direction theta, final Direction phi) throws ValueRuntimeException
81      {
82          double[] xyz = Scalar3d.polarToCartesian(speed.getInUnit(), theta.si, phi.si);
83          this.speed = new SpeedVector(xyz, speed.getDisplayUnit());
84      }
85  
86      /**
87       * Retrieve the x-component of this Speed3d.
88       * @return the speed in the x-direction.
89       */
90      public final Speed getX()
91      {
92          try
93          {
94              return this.speed.get(0);
95          }
96          catch (ValueRuntimeException exception)
97          {
98              // should be impossible as we constructed the vector always with three elements
99              throw new OtsRuntimeException(
100                     "getX() gave an exception; apparently vector " + this.speed + " was not constructed right", exception);
101         }
102     }
103 
104     /**
105      * Retrieve the y-component of this Speed3d.
106      * @return the speed in the y-direction.
107      */
108     public final Speed getY()
109     {
110         try
111         {
112             return this.speed.get(1);
113         }
114         catch (ValueRuntimeException exception)
115         {
116             // should be impossible as we constructed the vector always with three elements
117             throw new OtsRuntimeException(
118                     "getY() gave an exception; apparently vector " + this.speed + " was not constructed right", exception);
119         }
120     }
121 
122     /**
123      * Retrieve the z-component of this Speed3d.
124      * @return the speed in the z-direction.
125      */
126     public final Speed getZ()
127     {
128         try
129         {
130             return this.speed.get(2);
131         }
132         catch (ValueRuntimeException exception)
133         {
134             // should be impossible as we constructed the vector always with three elements
135             throw new OtsRuntimeException(
136                     "getZ() gave an exception; apparently vector " + this.speed + " was not constructed right", exception);
137         }
138     }
139 
140     /**
141      * Retrieve the theta of this Speed3d.
142      * @return the angle of direction perpendicular to the xy-plane
143      */
144     public final Direction getTheta()
145     {
146         return Scalar3d.cartesianToTheta(getX().si, getY().si, getZ().si);
147     }
148 
149     /**
150      * Retrieve the phi of this Speed3d.
151      * @return the projected angle of direction in the xy-plane
152      */
153     public final Direction getPhi()
154     {
155         return Scalar3d.cartesianToPhi(getX().si, getY().si);
156     }
157 
158     /**
159      * Retrieve the norm of this Speed3d.
160      * @return the combined speed in the direction of the angle
161      */
162     public final Speed getSpeed()
163     {
164         return new Speed(Scalar3d.cartesianToRadius(getX().si, getY().si, getZ().si), SpeedUnit.SI);
165     }
166 
167     @Override
168     public final String toString()
169     {
170         return String.format(Locale.US, "Speed3d %s (%s, theta %s, phi %s)", this.speed, getSpeed(),
171                 new Direction(getTheta().getInUnit(DirectionUnit.EAST_DEGREE), DirectionUnit.EAST_DEGREE),
172                 new Direction(getPhi().getInUnit(DirectionUnit.EAST_DEGREE), DirectionUnit.EAST_DEGREE));
173     }
174 
175 }