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.models.earth.displacement;
18  
19  import java.io.InputStream;
20  import java.util.ArrayList;
21  import java.util.List;
22  import java.util.Optional;
23  import java.util.stream.Collectors;
24  
25  import org.orekit.annotation.DefaultDataContext;
26  import org.orekit.data.AbstractSelfFeedingLoader;
27  import org.orekit.data.DataContext;
28  import org.orekit.data.DataLoader;
29  import org.orekit.data.DataProvidersManager;
30  import org.orekit.data.DataSource;
31  import org.orekit.errors.OrekitException;
32  import org.orekit.errors.OrekitMessages;
33  
34  /**
35   * Factory for ocean loading coefficients, using Onsala Space Observatory files in BLQ format.
36   * <p>
37   * Files in BLQ format can be generated using the form at the
38   * <a href="http://holt.oso.chalmers.se/loading/">Bos-Scherneck web site</a>,
39   * selecting BLQ as the output format.
40   * </p>
41   * <p>
42   * The sites names are extracted from the file content, not the file name, because the
43   * file can contain more than one station. As we expect existing files may have been
44   * stripped from headers and footers, we do not attempt to parse them. We only parse
45   * the series of 7 lines blocks starting with the lines with the station names and their
46   * coordinates and the 6 data lines that follows. Several such blocks may appear in the
47   * file. Copy-pasting the entire mail received from OSO after completing the web site
48   * form works, as intermediate lines between the 7 lines blocks are simply ignored.
49   * </p>
50   * @see OceanLoadingCoefficients
51   * @see OceanLoading
52   * @since 9.1
53   * @author Luc Maisonobe
54   */
55  public class OceanLoadingCoefficientsBLQFactory extends AbstractSelfFeedingLoader {
56  
57      /** Default supported files name pattern for Onsala Space Observatory files in BLQ format. */
58      public static final String DEFAULT_BLQ_SUPPORTED_NAMES = "^.+\\.blq$";
59  
60      /** Parsed coefficients. */
61      private final List<OceanLoadingCoefficients> coefficients;
62  
63      /** Simple constructor. This constructor uses the {@link DataContext#getDefault()
64       * default data context}.
65       * <p>
66       * Files in BLQ format can be generated using the form at the
67       * <a href="http://holt.oso.chalmers.se/loading/">Bos-Scherneck web site</a>,
68       * selecting BLQ as the output format.
69       * </p>
70       * @param supportedNames regular expression for supported files names
71       * @see #DEFAULT_BLQ_SUPPORTED_NAMES
72       * @see #OceanLoadingCoefficientsBLQFactory(String, DataProvidersManager)
73       */
74      @DefaultDataContext
75      public OceanLoadingCoefficientsBLQFactory(final String supportedNames) {
76          this(supportedNames, DataContext.getDefault().getDataProvidersManager());
77      }
78  
79      /**
80       * This constructor allows specification of the source of the BLQ auxiliary data
81       * files.
82       *
83       * <p>
84       * Files in BLQ format can be generated using the form at the
85       * <a href="http://holt.oso.chalmers.se/loading/">Bos-Scherneck web site</a>,
86       * selecting BLQ as the output format.
87       * </p>
88       * @param supportedNames regular expression for supported files names
89       * @param dataProvidersManager provides access to auxiliary data files.
90       * @see #DEFAULT_BLQ_SUPPORTED_NAMES
91       * @since 10.1
92       */
93      public OceanLoadingCoefficientsBLQFactory(final String supportedNames,
94                                                final DataProvidersManager dataProvidersManager) {
95          super(supportedNames, dataProvidersManager);
96  
97          this.coefficients   = new ArrayList<>();
98  
99      }
100 
101     /** Lazy loading of coefficients.
102      */
103     private void loadsIfNeeded() {
104         if (coefficients.isEmpty()) {
105             feed(new DataLoader() {
106 
107                 /** {@inheritDoc} */
108                 @Override
109                 public boolean stillAcceptsData() {
110                     return true;
111                 }
112 
113                 /** {@inheritDoc} */
114                 @Override
115                 public void loadData(final InputStream input, final String name) {
116                     final OceanLoadingCoefficientsBlqParser parser = new OceanLoadingCoefficientsBlqParser();
117                     coefficients.addAll(parser.parse(new DataSource(name, () -> input)));
118                 }
119             });
120         }
121     }
122 
123     /** Get the list of sites for which we have found coefficients, in lexicographic order ignoring case.
124      * @return list of sites for which we have found coefficients, in lexicographic order ignoring case
125      */
126     public List<String> getSites() {
127 
128         loadsIfNeeded();
129 
130         // extract sites names from the map
131         return coefficients.stream()
132                 .map(OceanLoadingCoefficients::getSiteName)
133                 // sort to ensure we have a reproducible order
134                 .sorted(String::compareToIgnoreCase)
135                 .collect(Collectors.toList());
136 
137     }
138 
139     /** Get the coefficients for a given site.
140      * @param site site name (as it appears in the Onsala Space Observatory files in BLQ format),
141      * ignoring case
142      * @return coefficients for the site
143      */
144     public OceanLoadingCoefficients getCoefficients(final String site) {
145 
146         loadsIfNeeded();
147 
148         final Optional<OceanLoadingCoefficients> optional =
149                         coefficients.stream().filter(c -> c.getSiteName().equalsIgnoreCase(site)).findFirst();
150         if (!optional.isPresent()) {
151             throw new OrekitException(OrekitMessages.STATION_NOT_FOUND,
152                                       site,
153                                       String.join(", ", getSites()));
154         }
155 
156         return optional.get();
157 
158     }
159 
160 }