OceanLoadingCoefficientsBlqParser.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 org.hipparchus.exception.LocalizedCoreFormats;
- import org.hipparchus.util.FastMath;
- import org.orekit.bodies.GeodeticPoint;
- import org.orekit.data.DataSource;
- import org.orekit.errors.OrekitException;
- import org.orekit.errors.OrekitMessages;
- import java.io.BufferedReader;
- import java.io.IOException;
- import java.util.ArrayList;
- import java.util.List;
- import java.util.regex.Matcher;
- import java.util.regex.Pattern;
- /**
- * Parser 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 OceanLoadingCoefficientsBlqParser {
- /** Pattern for fields with real type. */
- private static final String REAL_TYPE_PATTERN = "[-+]?(?:\\p{Digit}+(?:\\.\\p{Digit}*)?|\\.\\p{Digit}+)(?:[eE][-+]?\\p{Digit}+)?";
- /** Pattern for extracted real fields. */
- private static final String REAL_FIELD_PATTERN = "\\p{Space}*(" + REAL_TYPE_PATTERN + ")";
- /** Pattern for end of line. */
- private static final String END_OF_LINE_PATTERN = "\\p{Space}*$";
- /** Pattern for site name and coordinates lines. */
- private static final String SITE_LINE_PATTERN = "^\\$\\$ *([^,]*),\\p{Space}*(?:RADI TANG)?\\p{Space}*lon/lat:" +
- REAL_FIELD_PATTERN +
- REAL_FIELD_PATTERN +
- REAL_FIELD_PATTERN +
- END_OF_LINE_PATTERN;
- /** Pattern for coefficients lines. */
- private static final String DATA_LINE_PATTERN = "^" +
- REAL_FIELD_PATTERN + // M₂ tide
- REAL_FIELD_PATTERN + // S₂ tide
- REAL_FIELD_PATTERN + // N₂ tide
- REAL_FIELD_PATTERN + // K₂ tide
- REAL_FIELD_PATTERN + // K₁ tide
- REAL_FIELD_PATTERN + // O₁ tide
- REAL_FIELD_PATTERN + // P₁ tide
- REAL_FIELD_PATTERN + // Q₁ tide
- REAL_FIELD_PATTERN + // Mf tide
- REAL_FIELD_PATTERN + // Mm tide
- REAL_FIELD_PATTERN + // Ssa tide
- END_OF_LINE_PATTERN;
- /** Pattern for site name and coordinates lines. */
- private static final Pattern SITE_PATTERN = Pattern.compile(SITE_LINE_PATTERN);
- /** Pattern for coefficients lines. */
- private static final Pattern DATA_PATTERN = Pattern.compile(DATA_LINE_PATTERN);
- /** Main tides. */
- private static final Tide[][] TIDES = {
- {
- Tide.SSA, Tide.MM, Tide.MF
- }, {
- Tide.Q1, Tide.O1, Tide.P1, Tide.K1
- }, {
- Tide.N2, Tide.M2, Tide.S2, Tide.K2
- }
- };
- /** Species index for each column. */
- private static final int[] I = {
- 2, 2, 2, 2, 1, 1, 1, 1, 0, 0, 0
- };
- /** Tides index for each column. */
- private static final int[] J = {
- 1, 2, 0, 3, 3, 1, 2, 0, 2, 1, 0
- };
- /** Parse a BLQ file.
- * <p>
- * Files in BLQ format can be generated using the form at the
- * <a href="https://holt.oso.chalmers.se/loading/">Bos-Scherneck web site</a>,
- * selecting BLQ as the output format.
- * </p>
- * <p>
- * when completing the web site form, the email received as the following form:
- * </p>
- * <pre>{@literal
- * $$ Ocean loading displacement
- * $$
- * $$ Calculated on holt using olfg/olmpp of H.-G. Scherneck
- * $$
- * $$ COLUMN ORDER: M2 S2 N2 K2 K1 O1 P1 Q1 MF MM SSA
- * $$
- * $$ ROW ORDER:
- * $$ AMPLITUDES (m)
- * $$ RADIAL
- * $$ TANGENTL EW
- * $$ TANGENTL NS
- * $$ PHASES (degrees)
- * $$ RADIAL
- * $$ TANGENTL EW
- * $$ TANGENTL NS
- * $$
- * $$ Displacement is defined positive in upwards, South and West direction.
- * $$ The phase lag is relative to Greenwich and lags positive. The
- * $$ Gutenberg-Bullen Greens function is used. In the ocean tide model the
- * $$ deficit of tidal water mass has been corrected by subtracting a uniform
- * $$ layer of water with a certain phase lag globally.
- * $$
- * $$ Complete <model name> : No interpolation of ocean model was necessary
- * $$ <model name>_PP : Ocean model has been interpolated near the station
- * $$ (PP = Post-Processing)
- * $$
- * $$ Ocean tide model: CSR4.0, long-period tides from FES99
- * $$
- * $$ END HEADER
- * $$
- * Goldstone
- * $$ Complete CSR4.0_f
- * $$ Computed by OLFG, H.-G. Scherneck, Onsala Space Observatory 2017-Sep-28
- * $$ Goldstone, RADI TANG lon/lat: 243.1105 35.4259 0.000
- * .00130 .00155 .00064 .00052 .01031 .00661 .00339 .00119 .00005 .00002 .00003
- * .00136 .00020 .00024 .00004 .00322 .00202 .00106 .00036 .00007 .00003 .00001
- * .00372 .00165 .00082 .00045 .00175 .00113 .00057 .00022 .00004 .00002 .00003
- * -2.7 -106.3 -62.6 -106.8 41.6 27.3 40.4 24.0 -119.1 -123.2 -169.7
- * -145.3 -88.4 178.5 -66.3 -130.5 -145.3 -131.7 -148.7 124.3 139.6 23.3
- * 90.7 111.1 74.1 111.3 176.9 165.3 175.8 164.0 48.9 25.3 4.5
- * $$
- * ONSALA
- * $$ CSR4.0_f_PP ID: 2017-09-28 15:01:14
- * $$ Computed by OLMPP by H G Scherneck, Onsala Space Observatory, 2017
- * $$ Onsala, RADI TANG lon/lat: 11.9264 57.3958 0.000
- * .00344 .00121 .00078 .00031 .00189 .00116 .00064 .00004 .00090 .00048 .00041
- * .00143 .00035 .00035 .00008 .00053 .00051 .00018 .00009 .00013 .00006 .00007
- * .00086 .00023 .00023 .00006 .00029 .00025 .00010 .00008 .00003 .00001 .00000
- * -64.6 -50.3 -95.0 -53.1 -58.8 -152.4 -65.5 -133.8 9.8 5.8 2.1
- * 85.4 115.2 56.7 114.7 99.5 15.9 94.2 -10.0 -166.3 -169.8 -177.7
- * 110.7 147.1 93.9 148.6 49.4 -56.5 34.8 -169.9 -35.3 -3.7 10.1
- * $$ END TABLE
- * Errors:
- * Warnings:
- * }</pre>
- * <p>
- * We only parse blocks 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.
- * </p>
- * @param source source for BLQ data
- * @return parsed coefficients
- */
- public List<OceanLoadingCoefficients> parse(final DataSource source) {
- final List<OceanLoadingCoefficients> coefficients = new ArrayList<>();
- // temporary holders for parsed data
- String siteName = null;
- GeodeticPoint siteLocation = null;
- final double[][][] data = new double[6][3][];
- for (int i = 0; i < data.length; ++i) {
- data[i][0] = new double[3];
- data[i][1] = new double[4];
- data[i][2] = new double[4];
- }
- try (BufferedReader reader = new BufferedReader(
- source.getOpener().openReaderOnce())) {
- int lineNumber = 0;
- int dataLine = -1;
- for (String line = reader.readLine(); line != null; line = reader.readLine()) {
- ++lineNumber;
- if (dataLine < 0) {
- // we are looking for a site line
- final Matcher matcher = SITE_PATTERN.matcher(line);
- if (matcher.matches()) {
- // the current line is a site description line
- siteName = matcher.group(1);
- siteLocation =
- new GeodeticPoint(FastMath.toRadians(Double.parseDouble(matcher.group(3))),
- FastMath.toRadians(Double.parseDouble(matcher.group(2))),
- Double.parseDouble(matcher.group(4)));
- // next line must be line 0 of the data
- dataLine = 0;
- }
- } else {
- // we are looking for a data line
- final Matcher matcher = DATA_PATTERN.matcher(line);
- if (!matcher.matches()) {
- throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE,
- lineNumber, source.getName(), line);
- }
- for (int k = 0; k < I.length; ++k) {
- if (dataLine < 3) {
- // amplitude data
- data[dataLine][I[k]][J[k]] = Double.parseDouble(matcher.group(k + 1));
- } else {
- // phase data (reversed to be negative for lags)
- data[dataLine][I[k]][J[k]] = -FastMath.toRadians(Double.parseDouble(matcher.group(k + 1)));
- }
- }
- if (dataLine < data.length - 1) {
- // we need more data
- ++dataLine;
- } else {
- // it was the last data line
- coefficients.add(new OceanLoadingCoefficients(siteName, siteLocation,
- TIDES, data[0],
- data[3], data[1],
- data[4], data[2],
- data[5]));
- dataLine = -1;
- }
- }
- }
- if (dataLine >= 0) {
- // we were looking for a line that did not appear
- throw new OrekitException(OrekitMessages.UNEXPECTED_END_OF_FILE_AFTER_LINE,
- source.getName(), lineNumber);
- }
- } catch (IOException ioe) {
- throw new OrekitException(ioe, LocalizedCoreFormats.SIMPLE_MESSAGE,
- ioe.getLocalizedMessage());
- }
- return coefficients;
- }
- }