1   /* Copyright 2022-2025 Thales Alenia Space
2    * Licensed to CS Communication & Systèmes (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.troposphere.iturp834;
18  
19  import org.hipparchus.CalculusFieldElement;
20  
21  /** Holder for one cell of grid data surrounding one point.
22   * @param <T> type of the field elements
23   * @author Luc Maisonobe
24   * @since 13.0
25   */
26  class FieldGridCell<T extends CalculusFieldElement<T>> {
27  
28      /** Latitude difference with respect to South cell edge. */
29      private final T deltaSouth;
30  
31      /** Longitude difference with respect to West cell edge. */
32      private final T deltaWest;
33  
34      /** Cell size in latitude. */
35      private final double sizeLat;
36  
37      /** Cell size in longitude. */
38      private final double sizeLon;
39  
40      /** North-West value. */
41      private final T nw;
42  
43      /** South-West value. */
44      private final T sw;
45  
46      /** South-East value. */
47      private final T se;
48  
49      /** North-East value. */
50      private final T ne;
51  
52      /**
53       * Build a grid cell from corner data.
54       *
55       * @param deltaSouth point latitude minus South cell edge latitude
56       * @param deltaWest  point longitude minus West cell edge longitude
57       * @param sizeLat    cell size in latitude
58       * @param sizeLon    cell size in longitude
59       * @param nw         North-West value
60       * @param sw         South-West value
61       * @param se         South-East value
62       * @param ne         North-East value
63       */
64      FieldGridCell(final T deltaSouth, final T deltaWest, final double sizeLat, final double sizeLon,
65                    final T nw, final T sw, final T se, final T ne) {
66          this.deltaSouth = deltaSouth;
67          this.deltaWest  = deltaWest;
68          this.sizeLat    = sizeLat;
69          this.sizeLon    = sizeLon;
70          this.nw         = nw;
71          this.sw         = sw;
72          this.se         = se;
73          this.ne         = ne;
74      }
75  
76      /** Build a grid cell by applying a function to two existing cells.
77       * <p>
78       * The cells are expected to be consistent (i.e. same locations,
79       * same sizes), but no verification is done here. It works in
80       * the context of ITR-R P.834 because the grids have similar
81       * samplings, it would not work fro general and inconsistent grids.
82       * </p>
83       * @param function function to apply to all cells corners
84       * @param cell1 first cell
85       * @param cell2 second cell
86       */
87      FieldGridCell(final BiFunction<T> function,
88                    final FieldGridCell<T> cell1, final FieldGridCell<T> cell2) {
89          this.deltaSouth = cell1.deltaSouth;
90          this.deltaWest  = cell1.deltaWest;
91          this.sizeLat    = cell1.sizeLat;
92          this.sizeLon    = cell1.sizeLon;
93          this.nw         = function.apply(cell1.nw, cell2.nw);
94          this.sw         = function.apply(cell1.sw, cell2.sw);
95          this.se         = function.apply(cell1.se, cell2.se);
96          this.ne         = function.apply(cell1.ne, cell2.ne);
97      }
98  
99      /** Build a grid cell by applying a function to three existing cells.
100      * <p>
101      * The cells are expected to be consistent (i.e. same locations,
102      * same sizes), but no verification is done here. It works in
103      * the context of ITR-R P.834 because the grids have similar
104      * samplings, it would not work fro general and inconsistent grids.
105      * </p>
106      * @param function function to apply to all cells corners
107      * @param cell1 first cell
108      * @param cell2 second cell
109      * @param cell3 third cell
110      */
111     FieldGridCell(final TriFunction<T> function,
112                   final FieldGridCell<T> cell1, final FieldGridCell<T> cell2, final FieldGridCell<T> cell3) {
113         this.deltaSouth = cell1.deltaSouth;
114         this.deltaWest  = cell1.deltaWest;
115         this.sizeLat    = cell1.sizeLat;
116         this.sizeLon    = cell1.sizeLon;
117         this.nw         = function.apply(cell1.nw, cell2.nw, cell3.nw);
118         this.sw         = function.apply(cell1.sw, cell2.sw, cell3.sw);
119         this.se         = function.apply(cell1.se, cell2.se, cell3.se);
120         this.ne         = function.apply(cell1.ne, cell2.ne, cell3.ne);
121     }
122 
123     /** Build a grid cell by applying a function to four existing cells.
124      * <p>
125      * The cells are expected to be consistent (i.e. same locations,
126      * same sizes), but no verification is done here. It works in
127      * the context of ITR-R P.834 because the grids have similar
128      * samplings, it would not work fro general and inconsistent grids.
129      * </p>
130      * @param function function to apply to all cells corners
131      * @param cell1 first cell
132      * @param cell2 second cell
133      * @param cell3 third cell
134      * @param cell4 fourth cell
135      */
136     FieldGridCell(final QuarticFunction<T> function,
137                   final FieldGridCell<T> cell1, final FieldGridCell<T> cell2,
138                   final FieldGridCell<T> cell3, final FieldGridCell<T> cell4) {
139         this.deltaSouth = cell1.deltaSouth;
140         this.deltaWest  = cell1.deltaWest;
141         this.sizeLat    = cell1.sizeLat;
142         this.sizeLon    = cell1.sizeLon;
143         this.nw         = function.apply(cell1.nw, cell2.nw, cell3.nw, cell4.nw);
144         this.sw         = function.apply(cell1.sw, cell2.sw, cell3.sw, cell4.sw);
145         this.se         = function.apply(cell1.se, cell2.se, cell3.se, cell4.se);
146         this.ne         = function.apply(cell1.ne, cell2.ne, cell3.ne, cell4.ne);
147     }
148 
149     /** Build a grid cell by applying a function to five existing cells.
150      * <p>
151      * The cells are expected to be consistent (i.e. same locations,
152      * same sizes), but no verification is done here. It works in
153      * the context of ITR-R P.834 because the grids have similar
154      * samplings, it would not work fro general and inconsistent grids.
155      * </p>
156      * @param function function to apply to all cells corners
157      * @param cell1 first cell
158      * @param cell2 second cell
159      * @param cell3 third cell
160      * @param cell4 fourth cell
161      * @param cell5 fifth cell
162      */
163     FieldGridCell(final QuinticFunction<T> function,
164                   final FieldGridCell<T> cell1, final FieldGridCell<T> cell2, final FieldGridCell<T> cell3,
165                   final FieldGridCell<T> cell4, final FieldGridCell<T> cell5) {
166         this.deltaSouth = cell1.deltaSouth;
167         this.deltaWest  = cell1.deltaWest;
168         this.sizeLat    = cell1.sizeLat;
169         this.sizeLon    = cell1.sizeLon;
170         this.nw         = function.apply(cell1.nw, cell2.nw, cell3.nw, cell4.nw, cell5.nw);
171         this.sw         = function.apply(cell1.sw, cell2.sw, cell3.sw, cell4.sw, cell5.sw);
172         this.se         = function.apply(cell1.se, cell2.se, cell3.se, cell4.se, cell5.se);
173         this.ne         = function.apply(cell1.ne, cell2.ne, cell3.ne, cell4.ne, cell5.ne);
174     }
175 
176     /** Evaluate cell value at point location using bi-linear interpolation.
177      * @return cell value at point location
178      */
179     public T evaluate() {
180         final T deltaNorth = deltaSouth.negate().add(sizeLat);
181         final T deltaEast  = deltaWest.negate().add(sizeLon);
182         return     deltaSouth.multiply(deltaWest.multiply(ne).add(deltaEast.multiply(nw))).
183                add(deltaNorth.multiply(deltaWest.multiply(se).add(deltaEast.multiply(sw)))).
184                divide(sizeLat * sizeLon);
185     }
186 
187     /** Interface for function that can be applied to the corners of two cells. */
188     @FunctionalInterface
189     public interface BiFunction<T extends CalculusFieldElement<T>> {
190         /** Apply function to similar corners coming from two cells.
191          * @param corner1 value at corner of first cell
192          * @param corner2 value at corner of second cell
193          * @return function evaluated at similar corners of two cells
194          */
195         T apply(T corner1, T corner2);
196     }
197 
198     /** Interface for function that can be applied to the corners of three cells.
199      * @param <T> type of the field elements
200      */
201     @FunctionalInterface
202     public interface TriFunction<T extends CalculusFieldElement<T>> {
203         /** Apply function to similar corners coming from three cells.
204          * @param corner1 value at corner of first cell
205          * @param corner2 value at corner of second cell
206          * @param corner3 value at corner of third cell
207          * @return function evaluated at similar corners of three cells
208          */
209         T apply(T corner1, T corner2, T corner3);
210     }
211 
212     /** Interface for function that can be applied to the corners of four cells.
213      * @param <T> type of the field elements
214      */
215     @FunctionalInterface
216     public interface QuarticFunction<T extends CalculusFieldElement<T>> {
217         /** Apply function to similar corners coming from four cells.
218          * @param corner1 value at corner of first cell
219          * @param corner2 value at corner of second cell
220          * @param corner3 value at corner of third cell
221          * @param corner4 value at corner of fourth cell
222          * @return function evaluated at similar corners of four cells
223          */
224         T apply(T corner1, T corner2, T corner3, T corner4);
225     }
226 
227     /** Interface for function that can be applied to the corners of five cells.
228      * @param <T> type of the field elements
229      */
230     @FunctionalInterface
231     public interface QuinticFunction<T extends CalculusFieldElement<T>> {
232         /** Apply function to similar corners coming from five cells.
233          * @param corner1 value at corner of first cell
234          * @param corner2 value at corner of second cell
235          * @param corner3 value at corner of third cell
236          * @param corner4 value at corner of fourth cell
237          * @param corner5 value at corner of fifth cell
238          * @return function evaluated at similar corners of five cells
239          */
240         T apply(T corner1, T corner2, T corner3, T corner4, T corner5);
241     }
242 
243 }