1 /* Copyright 2002-2024 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.weather;
18
19 import java.util.List;
20 import java.util.SortedSet;
21
22 import org.hipparchus.CalculusFieldElement;
23 import org.hipparchus.util.FastMath;
24 import org.hipparchus.util.MathUtils;
25 import org.orekit.errors.OrekitException;
26 import org.orekit.errors.OrekitMessages;
27
28 /** Container for a complete grid.
29 * @author Bryan Cazabonne
30 * @author Luc Maisonobe
31 * @since 12.1
32 */
33 class Grid {
34
35 /** Latitude sample. */
36 private final SortedSet<Integer> latitudeSample;
37
38 /** Longitude sample. */
39 private final SortedSet<Integer> longitudeSample;
40
41 /** Grid entries. */
42 private final GridEntry[][] entries;
43
44 /** Simple constructor.
45 * @param latitudeSample latitude sample
46 * @param longitudeSample longitude sample
47 * @param loadedEntries loaded entries, organized as a simple list
48 * @param name file name
49 */
50 Grid(final SortedSet<Integer> latitudeSample, final SortedSet<Integer> longitudeSample,
51 final List<GridEntry> loadedEntries, final String name) {
52
53 final int nA = latitudeSample.size();
54 final int nO = longitudeSample.size() + 1; // we add one here for wrapping the grid
55 this.entries = new GridEntry[nA][nO];
56 this.latitudeSample = latitudeSample;
57 this.longitudeSample = longitudeSample;
58
59 // organize entries in the regular grid
60 for (final GridEntry entry : loadedEntries) {
61 final int latitudeIndex = latitudeSample.headSet(entry.getLatKey() + 1).size() - 1;
62 final int longitudeIndex = longitudeSample.headSet(entry.getLonKey() + 1).size() - 1;
63 entries[latitudeIndex][longitudeIndex] = entry;
64 }
65
66 // finalize the grid
67 for (final GridEntry[] row : entries) {
68
69 // check for missing entries
70 for (int longitudeIndex = 0; longitudeIndex < nO - 1; ++longitudeIndex) {
71 if (row[longitudeIndex] == null) {
72 throw new OrekitException(OrekitMessages.IRREGULAR_OR_INCOMPLETE_GRID, name);
73 }
74 }
75
76 // wrap the grid around the Earth in longitude
77 row[nO - 1] = row[0].buildWrappedEntry();
78
79 }
80
81 }
82
83 /** Get index of South entries in the grid.
84 * @param latitude latitude to locate (radians)
85 * @return index of South entries in the grid
86 */
87 private int getSouthIndex(final double latitude) {
88
89 final int latKey = (int) FastMath.rint(FastMath.toDegrees(latitude) * GridEntry.DEG_TO_MAS);
90 final int index = latitudeSample.headSet(latKey + 1).size() - 1;
91
92 // make sure we have at least one point remaining on North by clipping to size - 2
93 return FastMath.min(index, latitudeSample.size() - 2);
94
95 }
96
97 /** Get index of West entries in the grid.
98 * @param longitude longitude to locate (radians)
99 * @return index of West entries in the grid
100 */
101 private int getWestIndex(final double longitude) {
102
103 final int lonKey = (int) FastMath.rint(FastMath.toDegrees(longitude) * GridEntry.DEG_TO_MAS);
104 final int index = longitudeSample.headSet(lonKey + 1).size() - 1;
105
106 // we don't do clipping in longitude because we have added a row to wrap around the Earth
107 return index;
108
109 }
110
111 /** Get interpolator within a cell.
112 * @param latitude latitude of point of interest
113 * @param longitude longitude of point of interest
114 * @return interpolator for the cell
115 */
116 CellInterpolator getInterpolator(final double latitude, final double longitude) {
117
118 // keep longitude within grid range
119 final double normalizedLongitude =
120 MathUtils.normalizeAngle(longitude,
121 entries[0][0].getLongitude() + FastMath.PI);
122
123 // find neighboring grid entries
124 final int southIndex = getSouthIndex(latitude);
125 final int westIndex = getWestIndex(normalizedLongitude);
126
127 // build interpolator
128 return new CellInterpolator(latitude, normalizedLongitude,
129 entries[southIndex ][westIndex ],
130 entries[southIndex ][westIndex + 1],
131 entries[southIndex + 1][westIndex ],
132 entries[southIndex + 1][westIndex + 1]);
133
134 }
135
136 /** Get interpolator within a cell.
137 * @param <T> type of the field elements
138 * @param latitude latitude of point of interest
139 * @param longitude longitude of point of interest
140 * @return interpolator for the cell
141 */
142 <T extends CalculusFieldElement<T>> FieldCellInterpolator<T> getInterpolator(final T latitude, final T longitude) {
143
144 // keep longitude within grid range
145 final T normalizedLongitude =
146 MathUtils.normalizeAngle(longitude,
147 longitude.newInstance(entries[0][0].getLongitude() + FastMath.PI));
148
149 // find neighboring grid entries
150 final int southIndex = getSouthIndex(latitude.getReal());
151 final int westIndex = getWestIndex(normalizedLongitude.getReal());
152
153 // build interpolator
154 return new FieldCellInterpolator<>(latitude, normalizedLongitude,
155 entries[southIndex ][westIndex ],
156 entries[southIndex ][westIndex + 1],
157 entries[southIndex + 1][westIndex ],
158 entries[southIndex + 1][westIndex + 1]);
159
160 }
161
162 /** Check if grid contains all specified models.
163 * @param types models types
164 * @return true if grid contain the model
165 */
166 boolean hasModels(final SeasonalModelType... types) {
167 boolean hasAll = true;
168 for (final SeasonalModelType type : types) {
169 hasAll &= entries[0][0].getModel(type) != null;
170 }
171 return hasAll;
172 }
173
174 }