View Javadoc
1   /*
2    * Copyright (C) 2018 Alberto Irurueta Carro (alberto@irurueta.com)
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *         http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package com.irurueta.geometry;
18  
19  import com.irurueta.algebra.AlgebraException;
20  import com.irurueta.algebra.ArrayUtils;
21  import com.irurueta.algebra.Matrix;
22  import com.irurueta.algebra.NonSymmetricPositiveDefiniteMatrixException;
23  
24  /**
25   * Contains methods to convert covariance matrices into ellipsoids representing accuracy
26   * with requested confidence.
27   */
28  public class Accuracy3D extends Accuracy {
29  
30      /**
31       * Constructor.
32       */
33      public Accuracy3D() {
34          super();
35      }
36  
37      /**
38       * Constructor.
39       *
40       * @param covarianceMatrix covariance matrix to be set. Must be 3x3 and positive
41       *                         definite.
42       * @throws IllegalArgumentException                    if provided matrix is not square (it must also be
43       *                                                     positive definite to be properly converted to an ellipsoid).
44       * @throws NonSymmetricPositiveDefiniteMatrixException if provided matrix is not symmetric and
45       *                                                     positive definite.
46       */
47      public Accuracy3D(final Matrix covarianceMatrix) throws NonSymmetricPositiveDefiniteMatrixException {
48          super(covarianceMatrix);
49      }
50  
51      /**
52       * Constructor.
53       *
54       * @param confidence confidence of provided accuracy of an estimated position.
55       * @throws IllegalArgumentException if provided value is not within 0 and 1.
56       */
57      public Accuracy3D(final double confidence) {
58          super(confidence);
59      }
60  
61      /**
62       * Constructor.
63       *
64       * @param covarianceMatrix covariance matrix to be set. Must be 3x3 and positive
65       *                         definite.
66       * @param confidence       confidence of provided accuracy of an estimated position.
67       * @throws IllegalArgumentException                    if provided matrix is not square (it must also be
68       *                                                     positive definite to be properly converted to an ellipsoid),
69       *                                                     or if provided confidence value is not within 0 and 1.
70       * @throws NonSymmetricPositiveDefiniteMatrixException if provided matrix is not symmetric and
71       *                                                     positive definite.
72       */
73      public Accuracy3D(final Matrix covarianceMatrix, final double confidence)
74              throws NonSymmetricPositiveDefiniteMatrixException {
75          super(covarianceMatrix, confidence);
76      }
77  
78      /**
79       * Gets number of dimensions.
80       *
81       * @return always returns 3.
82       */
83      @Override
84      public int getNumberOfDimensions() {
85          return Point3D.POINT3D_INHOMOGENEOUS_COORDINATES_LENGTH;
86      }
87  
88      /**
89       * Converts provided covariance matrix into a 3D ellipsoid taking into account current
90       * confidence and standard deviation factor.
91       *
92       * @return ellipsoid representing accuracy of covariance matrix with current confidence and
93       * standard deviation factor.
94       * @throws NullPointerException           if covariance matrix has not been provided yet.
95       * @throws InvalidRotationMatrixException if rotation cannot be properly determined.
96       */
97      public Ellipsoid toEllipsoid() throws InvalidRotationMatrixException {
98          return toEllipsoid(standardDeviationFactor);
99      }
100 
101     /**
102      * Flattens accuracy representation to 2D by taking into account only x and y coordinates and
103      * ignoring variance related to z coordinates.
104      *
105      * @return flattened accuracy representation in 2D.
106      * @throws NullPointerException if covariance matrix is not defined.
107      * @throws GeometryException    if intersection cannot be computed.
108      */
109     public Accuracy2D flattenTo2D() throws GeometryException {
110         // get intersected ellipse for unitary standard deviation
111         final var ellipse = intersectWithPlane(1.0);
112 
113         final var semiMajorAxis = ellipse.getSemiMajorAxis();
114         final var semiMinorAxis = ellipse.getSemiMinorAxis();
115         final var rotation = ellipse.getRotation();
116 
117         final var u = rotation.asInhomogeneousMatrix();
118         final var s2 = Matrix.diagonal(new double[]{
119                 semiMajorAxis * semiMajorAxis,
120                 semiMinorAxis * semiMinorAxis});
121 
122         try {
123             // compute covariance as the squared matrix M = U*S*V'
124             // Hence: M*M' = U*S*V'*(U*S*V')' = U*S*V'*V'*S*U' = U*S^2*U'
125 
126             s2.multiply(u);
127             u.multiply(s2);
128 
129             return new Accuracy2D(u, confidence);
130         } catch (final AlgebraException e) {
131             throw new GeometryException(e);
132         }
133     }
134 
135     /**
136      * Intersects ellipsoid representing this accuracy with horizontal xy plane.
137      *
138      * @return intersected ellipse.
139      * @throws NullPointerException if covariance matrix is not defined.
140      * @throws GeometryException    if intersection cannot be computed.
141      */
142     public Ellipse intersectWithPlane() throws GeometryException {
143         return intersectWithPlane(standardDeviationFactor);
144     }
145 
146     /**
147      * Converts provided covariance matrix into a 3D ellipsoid taking into account current
148      * confidence and standard deviation factor.
149      *
150      * @param standardDeviationFactor standard deviation factor.
151      * @return ellipsoid representing accuracy of covariance matrix with provided standard
152      * deviation factor.
153      * @throws NullPointerException           if covariance matrix has not been provided yet.
154      * @throws InvalidRotationMatrixException if rotation cannot be properly determined.
155      */
156     private Ellipsoid toEllipsoid(final double standardDeviationFactor)
157             throws InvalidRotationMatrixException {
158         final var semiAxesLengths = ArrayUtils.multiplyByScalarAndReturnNew(
159                 sqrtSingularValues, standardDeviationFactor);
160         final var rotation = new MatrixRotation3D(u);
161         return new Ellipsoid(Point3D.create(), semiAxesLengths, rotation);
162     }
163 
164     /**
165      * Intersects ellipsoid representing this accuracy with provided standard
166      * deviation factor and with horizontal xy plane.
167      *
168      * @param standardDeviationFactor standard deviation factor.
169      * @return intersected ellipse.
170      * @throws NullPointerException if covariance matrix is not defined.
171      * @throws GeometryException    if intersection cannot be computed.
172      */
173     private Ellipse intersectWithPlane(final double standardDeviationFactor) throws GeometryException {
174         final var ellipsoid = toEllipsoid(standardDeviationFactor);
175         final var quadric = ellipsoid.toQuadric();
176 
177         // create horizontal xy plane located at ellipsoid center
178         final var center = ellipsoid.getCenter();
179         final var directorVector = new double[]{0.0, 0.0, 1.0};
180         final var plane = new Plane(center, directorVector);
181 
182         final var conic = quadric.intersectWith(plane);
183 
184         return new Ellipse(conic);
185     }
186 }