OceanLoadingCoefficientsBLQFactory.java

/* Copyright 2002-2024 CS GROUP
 * Licensed to CS GROUP (CS) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * CS licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.orekit.models.earth.displacement;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

import org.orekit.annotation.DefaultDataContext;
import org.orekit.data.AbstractSelfFeedingLoader;
import org.orekit.data.DataContext;
import org.orekit.data.DataLoader;
import org.orekit.data.DataProvidersManager;
import org.orekit.data.DataSource;
import org.orekit.errors.OrekitException;
import org.orekit.errors.OrekitMessages;

/**
 * Factory for ocean loading coefficients, using Onsala Space Observatory files in BLQ format.
 * <p>
 * Files in BLQ format can be generated using the form at the
 * <a href="http://holt.oso.chalmers.se/loading/">Bos-Scherneck web site</a>,
 * selecting BLQ as the output format.
 * </p>
 * <p>
 * The sites names are extracted from the file content, not the file name, because the
 * file can contain more than one station. As we expect existing files may have been
 * stripped from headers and footers, we do not attempt to parse them. We only parse
 * the series of 7 lines blocks starting with the lines with the station names and their
 * coordinates and the 6 data lines that follows. Several such blocks may appear in the
 * file. Copy-pasting the entire mail received from OSO after completing the web site
 * form works, as intermediate lines between the 7 lines blocks are simply ignored.
 * </p>
 * @see OceanLoadingCoefficients
 * @see OceanLoading
 * @since 9.1
 * @author Luc Maisonobe
 */
public class OceanLoadingCoefficientsBLQFactory extends AbstractSelfFeedingLoader {

    /** Default supported files name pattern for Onsala Space Observatory files in BLQ format. */
    public static final String DEFAULT_BLQ_SUPPORTED_NAMES = "^.+\\.blq$";

    /** Parsed coefficients. */
    private final List<OceanLoadingCoefficients> coefficients;

    /** Simple constructor. This constructor uses the {@link DataContext#getDefault()
     * default data context}.
     * <p>
     * Files in BLQ format can be generated using the form at the
     * <a href="http://holt.oso.chalmers.se/loading/">Bos-Scherneck web site</a>,
     * selecting BLQ as the output format.
     * </p>
     * @param supportedNames regular expression for supported files names
     * @see #DEFAULT_BLQ_SUPPORTED_NAMES
     * @see #OceanLoadingCoefficientsBLQFactory(String, DataProvidersManager)
     */
    @DefaultDataContext
    public OceanLoadingCoefficientsBLQFactory(final String supportedNames) {
        this(supportedNames, DataContext.getDefault().getDataProvidersManager());
    }

    /**
     * This constructor allows specification of the source of the BLQ auxiliary data
     * files.
     *
     * <p>
     * Files in BLQ format can be generated using the form at the
     * <a href="http://holt.oso.chalmers.se/loading/">Bos-Scherneck web site</a>,
     * selecting BLQ as the output format.
     * </p>
     * @param supportedNames regular expression for supported files names
     * @param dataProvidersManager provides access to auxiliary data files.
     * @see #DEFAULT_BLQ_SUPPORTED_NAMES
     * @since 10.1
     */
    public OceanLoadingCoefficientsBLQFactory(final String supportedNames,
                                              final DataProvidersManager dataProvidersManager) {
        super(supportedNames, dataProvidersManager);

        this.coefficients   = new ArrayList<>();

    }

    /** Lazy loading of coefficients.
     */
    private void loadsIfNeeded() {
        if (coefficients.isEmpty()) {
            feed(new DataLoader() {

                /** {@inheritDoc} */
                @Override
                public boolean stillAcceptsData() {
                    return true;
                }

                /** {@inheritDoc} */
                @Override
                public void loadData(final InputStream input, final String name) {
                    final OceanLoadingCoefficientsBlqParser parser = new OceanLoadingCoefficientsBlqParser();
                    coefficients.addAll(parser.parse(new DataSource(name, () -> input)));
                }
            });
        }
    }

    /** Get the list of sites for which we have found coefficients, in lexicographic order ignoring case.
     * @return list of sites for which we have found coefficients, in lexicographic order ignoring case
     */
    public List<String> getSites() {

        loadsIfNeeded();

        // extract sites names from the map
        return coefficients.stream()
                .map(OceanLoadingCoefficients::getSiteName)
                // sort to ensure we have a reproducible order
                .sorted(String::compareToIgnoreCase)
                .collect(Collectors.toList());

    }

    /** Get the coefficients for a given site.
     * @param site site name (as it appears in the Onsala Space Observatory files in BLQ format),
     * ignoring case
     * @return coefficients for the site
     */
    public OceanLoadingCoefficients getCoefficients(final String site) {

        loadsIfNeeded();

        final Optional<OceanLoadingCoefficients> optional =
                        coefficients.stream().filter(c -> c.getSiteName().equalsIgnoreCase(site)).findFirst();
        if (!optional.isPresent()) {
            throw new OrekitException(OrekitMessages.STATION_NOT_FOUND,
                                      site,
                                      String.join(", ", getSites()));
        }

        return optional.get();

    }

}