Grid.java
- /* Copyright 2002-2025 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.weather;
- import java.util.List;
- import java.util.SortedSet;
- import org.hipparchus.CalculusFieldElement;
- import org.hipparchus.util.FastMath;
- import org.hipparchus.util.FieldSinCos;
- import org.hipparchus.util.MathUtils;
- import org.hipparchus.util.SinCos;
- import org.orekit.errors.OrekitException;
- import org.orekit.errors.OrekitMessages;
- import org.orekit.utils.Constants;
- /** Container for a complete grid.
- * @author Bryan Cazabonne
- * @author Luc Maisonobe
- * @since 12.1
- */
- class Grid {
- /** Latitude sample. */
- private final SortedSet<Integer> latitudeSample;
- /** Longitude sample. */
- private final SortedSet<Integer> longitudeSample;
- /** Grid entries. */
- private final GridEntry[][] entries;
- /** Simple constructor.
- * @param latitudeSample latitude sample
- * @param longitudeSample longitude sample
- * @param loadedEntries loaded entries, organized as a simple list
- * @param name file name
- */
- Grid(final SortedSet<Integer> latitudeSample, final SortedSet<Integer> longitudeSample,
- final List<GridEntry> loadedEntries, final String name) {
- final int nA = latitudeSample.size();
- final int nO = longitudeSample.size() + 1; // we add one here for wrapping the grid
- this.entries = new GridEntry[nA][nO];
- this.latitudeSample = latitudeSample;
- this.longitudeSample = longitudeSample;
- // organize entries in the regular grid
- for (final GridEntry entry : loadedEntries) {
- final int latitudeIndex = latitudeSample.headSet(entry.getLatKey() + 1).size() - 1;
- final int longitudeIndex = longitudeSample.headSet(entry.getLonKey() + 1).size() - 1;
- entries[latitudeIndex][longitudeIndex] = entry;
- }
- // finalize the grid
- for (final GridEntry[] row : entries) {
- // check for missing entries
- for (int longitudeIndex = 0; longitudeIndex < nO - 1; ++longitudeIndex) {
- if (row[longitudeIndex] == null) {
- throw new OrekitException(OrekitMessages.IRREGULAR_OR_INCOMPLETE_GRID, name);
- }
- }
- // wrap the grid around the Earth in longitude
- row[nO - 1] = row[0].buildWrappedEntry();
- }
- }
- /** Get index of South entries in the grid.
- * @param latitude latitude to locate (radians)
- * @return index of South entries in the grid
- */
- private int getSouthIndex(final double latitude) {
- final int latKey = (int) FastMath.rint(FastMath.toDegrees(latitude) * GridEntry.DEG_TO_MAS);
- final int index = latitudeSample.headSet(latKey + 1).size() - 1;
- // make sure we have at least one point remaining on North by clipping to size - 2
- return FastMath.min(index, latitudeSample.size() - 2);
- }
- /** Get index of West entries in the grid.
- * @param longitude longitude to locate (radians)
- * @return index of West entries in the grid
- */
- private int getWestIndex(final double longitude) {
- final int lonKey = (int) FastMath.rint(FastMath.toDegrees(longitude) * GridEntry.DEG_TO_MAS);
- // we don't do clipping in longitude because we have added a row to wrap around the Earth
- return longitudeSample.headSet(lonKey + 1).size() - 1;
- }
- /** Get interpolator within a cell.
- * @param latitude latitude of point of interest
- * @param longitude longitude of point of interest
- * @param altitude altitude of point of interest
- * @param deltaRef duration since reference date
- * @return interpolator for the cell
- */
- CellInterpolator getInterpolator(final double latitude, final double longitude,
- final double altitude, final double deltaRef) {
- // keep longitude within grid range
- final double normalizedLongitude =
- MathUtils.normalizeAngle(longitude,
- entries[0][0].getLongitude() + FastMath.PI);
- // find neighboring grid entries
- final int southIndex = getSouthIndex(latitude);
- final int westIndex = getWestIndex(normalizedLongitude);
- final double coef = (deltaRef / Constants.JULIAN_YEAR) * 2 * FastMath.PI;
- final SinCos sc1 = FastMath.sinCos(coef);
- final SinCos sc2 = FastMath.sinCos(2.0 * coef);
- // build interpolator
- return new CellInterpolator(latitude, normalizedLongitude,
- entries[southIndex ][westIndex ].evaluate(sc1, sc2, altitude),
- entries[southIndex ][westIndex + 1].evaluate(sc1, sc2, altitude),
- entries[southIndex + 1][westIndex ].evaluate(sc1, sc2, altitude),
- entries[southIndex + 1][westIndex + 1].evaluate(sc1, sc2, altitude));
- }
- /** Get interpolator within a cell.
- * @param <T> type of the field elements
- * @param latitude latitude of point of interest
- * @param longitude longitude of point of interest
- * @param altitude altitude of point of interest
- * @param deltaRef duration since reference date
- * @return interpolator for the cell
- */
- <T extends CalculusFieldElement<T>> FieldCellInterpolator<T> getInterpolator(final T latitude, final T longitude,
- final T altitude, final T deltaRef) {
- // keep longitude within grid range
- final T normalizedLongitude =
- MathUtils.normalizeAngle(longitude,
- longitude.newInstance(entries[0][0].getLongitude() + FastMath.PI));
- // find neighboring grid entries
- final int southIndex = getSouthIndex(latitude.getReal());
- final int westIndex = getWestIndex(normalizedLongitude.getReal());
- final T coef = deltaRef.multiply(2 * FastMath.PI / Constants.JULIAN_YEAR);
- final FieldSinCos<T> sc1 = FastMath.sinCos(coef);
- final FieldSinCos<T> sc2 = FastMath.sinCos(coef.multiply(2));
- // build interpolator
- return new FieldCellInterpolator<>(latitude, normalizedLongitude,
- entries[southIndex ][westIndex ].evaluate(sc1, sc2, altitude),
- entries[southIndex ][westIndex + 1].evaluate(sc1, sc2, altitude),
- entries[southIndex + 1][westIndex ].evaluate(sc1, sc2, altitude),
- entries[southIndex + 1][westIndex + 1].evaluate(sc1, sc2, altitude));
- }
- /** Check if grid contains all specified models.
- * @param types models types
- * @return true if grid contain the model
- */
- boolean hasModels(final SeasonalModelType... types) {
- boolean hasAll = true;
- for (final SeasonalModelType type : types) {
- hasAll &= entries[0][0].hasModel(type);
- }
- return hasAll;
- }
- }