1   /* Copyright 2011-2012 Space Applications Services
2    * Licensed to CS Communication & Systèmes (CS) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * CS licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *   http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.orekit.utils;
18  
19  import java.io.BufferedReader;
20  import java.io.IOException;
21  import java.io.InputStream;
22  import java.io.InputStreamReader;
23  import java.io.StreamTokenizer;
24  import java.nio.charset.StandardCharsets;
25  import java.text.ParseException;
26  import java.util.LinkedList;
27  import java.util.List;
28  
29  import org.orekit.data.DataLoader;
30  
31  /** Used to read an interpolation table from a data file.
32   * @author Thomas Neidhart
33   */
34  public class InterpolationTableLoader implements DataLoader {
35  
36      /** Abscissa grid for the bi-variate interpolation function read from the file. */
37      private double[] xArr;
38  
39      /** Ordinate grid for the bi-variate interpolation function read from the file. */
40      private double[] yArr;
41  
42      /** Values samples for the bi-variate interpolation function read from the file. */
43      private double[][] fArr;
44  
45      /** Empty constructor.
46       * <p>
47       * This constructor is not strictly necessary, but it prevents spurious
48       * javadoc warnings with JDK 18 and later.
49       * </p>
50       * @since 12.0
51       */
52      public InterpolationTableLoader() {
53          // nothing to do
54      }
55  
56      /** Returns a copy of the abscissa grid for the interpolation function.
57       * @return the abscissa grid for the interpolation function,
58       *         or <code>null</code> if the file could not be read
59       */
60      public double[] getAbscissaGrid() {
61          return xArr.clone();
62      }
63  
64      /** Returns a copy of the ordinate grid for the interpolation function.
65       * @return the ordinate grid for the interpolation function,
66       *         or <code>null</code> if the file could not be read
67       */
68      public double[] getOrdinateGrid() {
69          return yArr.clone();
70      }
71  
72      /** Returns a copy of the values samples for the interpolation function.
73       * @return the values samples for the interpolation function,
74       *         or <code>null</code> if the file could not be read
75       */
76      public double[][] getValuesSamples() {
77          return fArr.clone();
78      }
79  
80      /** {@inheritDoc} */
81      public boolean stillAcceptsData() {
82          return xArr == null;
83      }
84  
85      /** Loads an bi-variate interpolation table from the given {@link InputStream}.
86       * The format of the table is as follows (number of rows/columns can be extended):
87       * <pre>
88       *  Table: tableName
89       *
90       *      | 0.0 |  60.0 |  66.0
91       *  -------------------------
92       *    0 | 0.0 | 0.003 | 0.006
93       *  500 | 0.0 | 0.003 | 0.006
94       * </pre>
95       * @param input the input stream to read data from
96       * @param name  the name of the input file
97       * @exception IOException if data can't be read
98       * @exception ParseException if data can't be parsed
99       */
100     public void loadData(final InputStream input, final String name)
101         throws IOException, ParseException {
102 
103         final List<Double> xValues = new LinkedList<>();
104         final List<Double> yValues = new LinkedList<>();
105         final LinkedList<List<Double>> cellValues = new LinkedList<>();
106 
107         final StreamTokenizer tokenizer =
108             new StreamTokenizer(new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8)));
109 
110         // ignore comments starting with a #
111         tokenizer.commentChar('#');
112         tokenizer.eolIsSignificant(true);
113 
114         int tokenCount = 0;
115         boolean headerRow = false;
116         boolean done = false;
117 
118         do {
119             switch (tokenizer.nextToken()) {
120 
121                 case StreamTokenizer.TT_EOF:
122                     done = true;
123                     break;
124 
125                 case StreamTokenizer.TT_EOL:
126                     // end of header row
127                     if (yValues.size() > 0) {
128                         headerRow = false;
129                     }
130                     tokenCount = 0;
131                     break;
132 
133                 case StreamTokenizer.TT_NUMBER:
134                     if (headerRow) {
135                         yValues.add(tokenizer.nval);
136                     } else {
137                         if (tokenCount == 0) {
138                             xValues.add(tokenizer.nval);
139                             cellValues.add(new LinkedList<>());
140                         } else {
141                             cellValues.getLast().add(tokenizer.nval);
142                         }
143                     }
144                     tokenCount++;
145                     break;
146 
147                 case StreamTokenizer.TT_WORD:
148                     // we are in the header row now
149                     if (tokenizer.sval.startsWith("Table")) {
150                         headerRow = true;
151                     }
152                     break;
153 
154                 default:
155                     break;
156             }
157 
158         } while (!done);
159 
160         xArr = toPrimitiveArray(xValues);
161         yArr = toPrimitiveArray(yValues);
162         fArr = new double[cellValues.size()][];
163         int idx = 0;
164 
165         for (List<Double> row : cellValues) {
166             fArr[idx++] = toPrimitiveArray(row);
167         }
168 
169     }
170 
171     /** Converts a list of {@link Double} objects into an array of double primitives.
172      * @param list the list of {@link Double} objects
173      * @return the double array containing the list elements
174      */
175     private double[] toPrimitiveArray(final List<Double> list) {
176         final double[] result = new double[list.size()];
177         int idx = 0;
178         for (Double element : list) {
179             result[idx++] = element;
180         }
181         return result;
182     }
183 }