AstronomicalAmplitudeReader.java

  1. /* Copyright 2002-2025 CS GROUP
  2.  * Licensed to CS GROUP (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.forces.gravity.potential;

  18. import java.io.BufferedReader;
  19. import java.io.IOException;
  20. import java.io.InputStream;
  21. import java.io.InputStreamReader;
  22. import java.nio.charset.StandardCharsets;
  23. import java.util.Collections;
  24. import java.util.HashMap;
  25. import java.util.Map;
  26. import java.util.regex.Matcher;
  27. import java.util.regex.Pattern;

  28. import org.hipparchus.util.FastMath;
  29. import org.orekit.data.DataLoader;
  30. import org.orekit.errors.OrekitException;
  31. import org.orekit.errors.OrekitMessages;

  32. /**
  33.  * Parser for tides astronomical amplitude H<sub>f</sub>.
  34.  * @author Luc Maisonobe
  35.  * @since 6.1
  36.  */
  37. public class AstronomicalAmplitudeReader implements DataLoader {

  38.     /** Pattern for optional fields (either nothing or non-space characters). */
  39.     private static final String  OPTIONAL_FIELD_PATTERN = "\\S*";

  40.     /** Pattern for fields with Doodson number. */
  41.     private static final String  DOODSON_TYPE_PATTERN = "\\p{Digit}{2,3}[.,]\\p{Digit}{3}";

  42.     /** Pattern for fields with real type. */
  43.     private static final String  REAL_TYPE_PATTERN =
  44.             "[-+]?(?:(?:\\p{Digit}+(?:\\.\\p{Digit}*)?)|(?:\\.\\p{Digit}+))(?:[eE][-+]?\\p{Digit}+)?";

  45.     /** Pattern for regular data. */
  46.     private static final Pattern PATTERN = Pattern.compile("[.,]");

  47.     /** Regular expression for supported files names. */
  48.     private final String supportedNames;

  49.     /** Pattern for regular data lines. */
  50.     private final Pattern regularLinePattern;

  51.     /** Doodson number column. */
  52.     private final int columnDoodson;

  53.     /** H<sub>f</sub> column. */
  54.     private final int columnHf;

  55.     /** Scaling factor for astronomical amplitude. */
  56.     private final double scale;

  57.     /** Amplitudes map. */
  58.     private final Map<Integer, Double> amplitudesMap;

  59.     /** Simple constructor.
  60.      * @param supportedNames regular expression for supported files names
  61.      * @param columns number of columns
  62.      * @param columnDoodson Doodson number column (counting from 1)
  63.      * @param columnHf H<sub>f</sub> column (counting from 1)
  64.      * @param scale scaling factor for astronomical amplitude
  65.      */
  66.     public AstronomicalAmplitudeReader(final String supportedNames, final int columns,
  67.                                        final int columnDoodson, final int columnHf,
  68.                                        final double scale) {

  69.         // build the pattern for the regular data lines
  70.         final StringBuilder builder = new StringBuilder("^\\p{Space}*");
  71.         for (int i = 1; i <= columns; ++i) {
  72.             builder.append("(");
  73.             if (i == columnDoodson) {
  74.                 builder.append(DOODSON_TYPE_PATTERN);
  75.             } else if (i == columnHf) {
  76.                 builder.append(REAL_TYPE_PATTERN);
  77.             } else {
  78.                 builder.append(OPTIONAL_FIELD_PATTERN);
  79.             }
  80.             builder.append(")");
  81.             builder.append(i < FastMath.max(columnDoodson, columnHf) ? "\\p{Space}+" : "\\p{Space}*");
  82.         }
  83.         builder.append('$');
  84.         this.regularLinePattern = Pattern.compile(builder.toString());

  85.         this.supportedNames = supportedNames;
  86.         this.columnDoodson  = columnDoodson;
  87.         this.columnHf       = columnHf;
  88.         this.scale          = scale;

  89.         this.amplitudesMap  = new HashMap<>();

  90.     }

  91.     /** Get the regular expression for supported files names.
  92.      * @return regular expression for supported files names
  93.      */
  94.     public String getSupportedNames() {
  95.         return supportedNames;
  96.     }

  97.     /** {@inheritDoc} */
  98.     @Override
  99.     public boolean stillAcceptsData() {
  100.         return amplitudesMap.isEmpty();
  101.     }

  102.     /** {@inheritDoc} */
  103.     @Override
  104.     public void loadData(final InputStream input, final String name)
  105.         throws IOException {

  106.         int lineNumber = 0;
  107.         String line = null;
  108.         // parse the file
  109.         try (BufferedReader r = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8))) {

  110.             for (line = r.readLine(); line != null; line = r.readLine()) {
  111.                 ++lineNumber;

  112.                 // replace unicode minus sign ('−') by regular hyphen ('-') for parsing
  113.                 // such unicode characters occur in tables that are copy-pasted from PDF files
  114.                 line = line.replace('\u2212', '-');

  115.                 final Matcher regularMatcher = regularLinePattern.matcher(line);
  116.                 if (regularMatcher.matches()) {
  117.                     // we have found a regular data line
  118.                     final int    doodson = Integer.parseInt(PATTERN.matcher(regularMatcher.group(columnDoodson)).replaceAll(""));
  119.                     final double hf      = scale * Double.parseDouble(regularMatcher.group(columnHf));
  120.                     amplitudesMap.put(doodson, hf);
  121.                 }
  122.             }

  123.         } catch (NumberFormatException nfe) {
  124.             throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE,
  125.                                       lineNumber, name, line);
  126.         }

  127.         if (amplitudesMap.isEmpty()) {
  128.             throw new OrekitException(OrekitMessages.NOT_A_SUPPORTED_IERS_DATA_FILE, name);
  129.         }

  130.     }

  131.     /** Get astronomical amplitudes map.
  132.      * @return an unmodifiable map containing astronomical amplitudes H<sub>f</sub>
  133.      * from a Doodson number key
  134.      */
  135.     public Map<Integer, Double> getAstronomicalAmplitudesMap() {
  136.         return Collections.unmodifiableMap(amplitudesMap);
  137.     }

  138. }