View Javadoc
1   package org.opentrafficsim.core.math;
2   
3   import java.util.Locale;
4   
5   import org.djunits.unit.AngleUnit;
6   import org.djunits.unit.SpeedUnit;
7   import org.djunits.value.StorageType;
8   import org.djunits.value.ValueException;
9   import org.djunits.value.vdouble.scalar.Angle;
10  import org.djunits.value.vdouble.scalar.Speed;
11  import org.djunits.value.vdouble.vector.SpeedVector;
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-2015 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
24   * BSD-style license. See <a href="http://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
25   * </p>
26   * $LastChangedDate: 2015-07-24 02:58:59 +0200 (Fri, 24 Jul 2015) $, @version $Revision: 1147 $, by $Author: averbraeck $,
27   * initial version Dec 10, 2015 <br>
28   * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
29   * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
30   */
31  public class Speed3D
32  {
33      /** The speed in 3D (XYZ coded). */
34      private final SpeedVector speed;
35  
36      /**
37       * Construct a new Speed3D from vector of strongly typed Cartesian coordinates.
38       * @param speed the speeds in 3D (YPR coded)
39       * @throws ValueException in case the vector does not have exactly three elements
40       */
41      public Speed3D(final SpeedVector speed) throws ValueException
42      {
43          super();
44          if (speed.size() != 3)
45          {
46              throw new ValueException("Size of an RPY-speed vector should be exactly 3. Got: " + speed);
47          }
48          this.speed = speed;
49      }
50  
51      /**
52       * Construct a new Speed3D from three strongly typed Cartesian coordinates.
53       * @param x the speed in the x-direction
54       * @param y the speed in the y-direction
55       * @param z the speed in the z-direction
56       * @throws ValueException in case the units are incorrect
57       */
58      public Speed3D(final Speed x, final Speed y, final Speed z) throws ValueException
59      {
60          super();
61          this.speed = new SpeedVector(new Speed[]{x, y, z}, StorageType.DENSE);
62      }
63  
64      /**
65       * Construct a new Speed3D from three double Cartesian coordinates and a speed unit.
66       * @param x the speed in the x-direction
67       * @param y the speed in the y-direction
68       * @param z the speed in the z-direction
69       * @param unit the unit of the xyz parameters
70       * @throws ValueException in case the units are incorrect
71       */
72      public Speed3D(final double x, final double y, final double z, final SpeedUnit unit) throws ValueException
73      {
74          super();
75          this.speed = new SpeedVector(new double[]{x, y, z}, unit, StorageType.DENSE);
76      }
77  
78      /**
79       * Construct a new Speed3D from a strongly typed speed and polar coordinates.
80       * @param speed Speed; the speed in the direction of the angle along the vector
81       * @param theta Angle.Abs; the angle from the z direction
82       * @param phi Angle.Abs; the projected angle in the xy-plane from the x direction
83       * @throws ValueException in case the vector does not have exactly three elements
84       */
85      public Speed3D(final Speed speed, final Angle.Abs theta, final Angle.Abs phi) throws ValueException
86      {
87          super();
88          double[] xyz = Scalar3D.polarToCartesian(speed.getInUnit(), theta.si, phi.si);
89          this.speed = new SpeedVector(xyz, speed.getUnit(), StorageType.DENSE);
90      }
91  
92      /**
93       * Retrieve the x-component of this Speed3D.
94       * @return the speed in the x-direction.
95       */
96      public final Speed getX()
97      {
98          try
99          {
100             return this.speed.get(0);
101         }
102         catch (ValueException exception)
103         {
104             // should be impossible as we constructed the vector always with three elements
105             throw new RuntimeException("getX() gave an exception; apparently vector " + this.speed
106                 + " was not constructed right", exception);
107         }
108     }
109 
110     /**
111      * Retrieve the y-component of this Speed3D.
112      * @return the speed in the y-direction.
113      */
114     public final Speed getY()
115     {
116         try
117         {
118             return this.speed.get(1);
119         }
120         catch (ValueException exception)
121         {
122             // should be impossible as we constructed the vector always with three elements
123             throw new RuntimeException("getY() gave an exception; apparently vector " + this.speed
124                 + " was not constructed right", exception);
125         }
126     }
127 
128     /**
129      * Retrieve the z-component of this Speed3D.
130      * @return the speed in the z-direction.
131      */
132     public final Speed getZ()
133     {
134         try
135         {
136             return this.speed.get(2);
137         }
138         catch (ValueException exception)
139         {
140             // should be impossible as we constructed the vector always with three elements
141             throw new RuntimeException("getZ() gave an exception; apparently vector " + this.speed
142                 + " was not constructed right", exception);
143         }
144     }
145 
146     /**
147      * Retrieve the theta of this Speed3D.
148      * @return the angle of direction perpendicular to the xy-plane
149      */
150     public final Angle.Abs getTheta()
151     {
152         return Scalar3D.cartesianToTheta(getX().si, getY().si, getZ().si);
153     }
154 
155     /**
156      * Retrieve the phi of this Speed3D.
157      * @return the projected angle of direction in the xy-plane
158      */
159     public final Angle.Abs getPhi()
160     {
161         return Scalar3D.cartesianToPhi(getX().si, getY().si);
162     }
163 
164     /**
165      * Retrieve the norm of this Speed3D.
166      * @return the combined speed in the direction of the angle
167      */
168     public final Speed getSpeed()
169     {
170         return new Speed(Scalar3D.cartesianToRadius(getX().si, getY().si, getZ().si), SpeedUnit.SI);
171     }
172 
173     /** {@inheritDoc} */
174     public final String toString()
175     {
176         return String.format(Locale.US, "Speed3D %s (%s, theta %s, phi %s)", this.speed, getSpeed(), new Angle.Abs(
177             getTheta().getInUnit(AngleUnit.DEGREE), AngleUnit.DEGREE),
178             new Angle.Abs(getPhi().getInUnit(AngleUnit.DEGREE), AngleUnit.DEGREE));
179     }
180 
181 }