001/*
002 * This file is part of McIDAS-V
003 *
004 * Copyright 2007-2024
005 * Space Science and Engineering Center (SSEC)
006 * University of Wisconsin - Madison
007 * 1225 W. Dayton Street, Madison, WI 53706, USA
008 * https://www.ssec.wisc.edu/mcidas/
009 * 
010 * All Rights Reserved
011 * 
012 * McIDAS-V is built on Unidata's IDV and SSEC's VisAD libraries, and
013 * some McIDAS-V source code is based on IDV and VisAD source code.  
014 * 
015 * McIDAS-V is free software; you can redistribute it and/or modify
016 * it under the terms of the GNU Lesser Public License as published by
017 * the Free Software Foundation; either version 3 of the License, or
018 * (at your option) any later version.
019 * 
020 * McIDAS-V is distributed in the hope that it will be useful,
021 * but WITHOUT ANY WARRANTY; without even the implied warranty of
022 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
023 * GNU Lesser Public License for more details.
024 * 
025 * You should have received a copy of the GNU Lesser Public License
026 * along with this program.  If not, see https://www.gnu.org/licenses/.
027 */
028
029package edu.wisc.ssec.mcidasv.data.hydra;
030
031import java.util.HashMap;
032import java.util.Map;
033import java.util.Objects;
034
035import ucar.ma2.DataType;
036import visad.CoordinateSystem;
037import visad.Gridded2DDoubleSet;
038import visad.Gridded2DSet;
039import visad.Linear2DSet;
040import visad.RealTupleType;
041
042public class SwathNavigation implements Navigation  {
043
044  public static SwathNavigation createNavigation(SwathAdapter swathAdapter) throws Exception {
045    String product_name = null;
046    SwathNavigation swathNav = null;
047    
048    product_name = (String)swathAdapter.getMetadata().get(SwathAdapter.product_name);
049
050    if (product_name == null) {
051      swathNav = new SwathNavigation(swathAdapter);
052    }
053    else if (Objects.equals(product_name, "IASI_L1C_xxx")) {
054      swathNav = new IASI_L1C_LonLatNavigation(swathAdapter);
055    }
056    else if (Objects.equals(product_name, "CrIS_SDR")) {
057      swathNav = new CrIS_SDR_LonLatNavigation(swathAdapter);
058    }
059    else {
060      swathNav = new SwathNavigation(swathAdapter);
061    }
062    
063    return swathNav;
064  }
065
066  int geo_track_idx;
067  int geo_xtrack_idx;
068  int geoTrackLen;
069  int geoXTrackLen;
070
071  SwathAdapter swathAdapter;
072  MultiDimensionReader reader;
073  String lon_array_name;
074  String lat_array_name;
075  int[] idx_order = new int[2];
076  float ratio = 1;
077  float track_ratio = 1;
078  float xtrack_ratio = 1;
079  double track_offset = 0;
080  double xtrack_offset = 0;
081  int track_idx;
082  int xtrack_idx;
083  int[] geo_stride = new int[2];
084  int[] geo_count = new int[2];
085  int[] geo_start = new int[2];
086
087  String scale_name = "SCALE_NAME";
088  String offset_name = "OFFSET_NAME";
089  String fillValue_name = "_FILLVALUE";
090
091  int numDims = 2;
092
093  DataType type;
094
095  public SwathNavigation(SwathAdapter swathAdapter) throws Exception {
096
097    Map<String, Object> metadata = swathAdapter.getMetadata();
098    reader = swathAdapter.getReader();
099    this.swathAdapter = swathAdapter;
100    track_idx = swathAdapter.track_idx;
101    xtrack_idx = swathAdapter.xtrack_idx;
102
103    lon_array_name = (String)metadata.get(SwathAdapter.lon_array_name);
104    lat_array_name = (String)metadata.get(SwathAdapter.lat_array_name);
105
106    String[] lon_dim_names = null;
107
108    String[] lonDimNames = (String[])metadata.get(SwathAdapter.lon_array_dimension_names);
109
110    if (lonDimNames != null) {
111      lon_dim_names = lonDimNames;
112    }
113    else {
114      lon_dim_names = reader.getDimensionNames(lon_array_name);
115    }
116
117    int[] lon_dim_lengths = reader.getDimensionLengths(lon_array_name);
118
119    numDims = lon_dim_lengths.length;
120    geo_stride = new int[numDims];
121    geo_count = new int[numDims];
122    geo_start = new int[numDims];
123
124
125    String geo_track_name = (String)metadata.get(SwathAdapter.geo_track_name);
126    String geo_xtrack_name = (String)metadata.get(SwathAdapter.geo_xtrack_name);
127
128    for (int k=0; k<numDims;k++) {
129      if ( geo_track_name.equals(lon_dim_names[k]) ) {
130         geo_track_idx = k;
131      }
132      if ( geo_xtrack_name.equals(lon_dim_names[k]) ) {
133         geo_xtrack_idx = k;
134      }
135    }
136
137    if (geo_track_idx < geo_xtrack_idx) {
138      idx_order[0] = geo_xtrack_idx;
139      idx_order[1] = geo_track_idx;
140    }
141    else {
142      idx_order[0] = geo_track_idx;
143      idx_order[1] = geo_xtrack_idx;
144    }
145
146    geoTrackLen  = lon_dim_lengths[geo_track_idx];
147    geoXTrackLen = lon_dim_lengths[geo_xtrack_idx];
148
149    String str = (String)metadata.get(SwathAdapter.geo_track_skip_name);
150
151    if (str != null) {
152      track_ratio = (float) Double.parseDouble(str);
153      ratio = track_ratio;
154    }
155    str = (String)metadata.get(SwathAdapter.geo_xtrack_skip_name);
156    if (str != null) {
157      xtrack_ratio = (float) Double.parseDouble(str);
158    }
159    str = (String)metadata.get(SwathAdapter.geo_track_offset_name);
160    if (str != null) {
161      track_offset = Double.parseDouble(str);
162    }
163    str = (String)metadata.get(SwathAdapter.geo_xtrack_offset_name);
164    if (str != null) {
165      xtrack_offset = Double.parseDouble(str);
166    }
167
168    str = (String)metadata.get(SwathAdapter.geo_scale_name);
169    if (str != null) {
170      scale_name = str;
171    }
172
173    str = (String)metadata.get(SwathAdapter.geo_offset_name);
174    if (str != null) {
175      offset_name = str;
176    }
177
178    str = (String)metadata.get(SwathAdapter.geo_fillValue_name);
179    if (str != null) {
180      fillValue_name = str;
181    }
182 
183    type = reader.getArrayType(lon_array_name);
184  }
185
186  public CoordinateSystem getVisADCoordinateSystem(Linear2DSet domainSet, Map<String, double[]> domainSubset) throws Exception
187  {
188      Subset select = swathAdapter.getIndexes(domainSubset);
189
190      double[] track_coords = domainSubset.get(SwathAdapter.track_name);
191      double[] xtrack_coords = domainSubset.get(SwathAdapter.xtrack_name);
192      
193      int[] stride = new int[numDims];
194      stride[geo_track_idx] = (int) track_coords[2];
195      stride[geo_xtrack_idx] = (int) xtrack_coords[2]; 
196
197
198      if (numDims > 2) { // initialize geo arrays, then recompute xtrack/track dimensions below
199        if (numDims == select.getRank()) {
200          int[] start = select.getStart();
201          int[] count = select.getCount();
202          stride = select.getStride();
203          for (int i=0; i<numDims; i++) {
204            geo_start[i] = start[i];
205            geo_count[i] = count[i];
206            geo_stride[i] = stride[i];
207          }
208        }
209        else {
210          geo_start[geo_track_idx] = (int) track_coords[0];
211          geo_start[geo_xtrack_idx] = (int) xtrack_coords[0];
212          geo_count[geo_track_idx] = (int) ((track_coords[1] - track_coords[0])/track_coords[2] + 1f);
213          geo_count[geo_xtrack_idx] = (int) ((xtrack_coords[1] - xtrack_coords[0])/xtrack_coords[2] + 1f);
214        }
215      }
216
217
218      if (ratio/(float)stride[0] <= 1) {
219        geo_stride[geo_track_idx] = Math.round((1f/(track_ratio/((float)stride[geo_track_idx]))));
220        geo_stride[geo_xtrack_idx] = Math.round((1f/(xtrack_ratio/((float)stride[geo_xtrack_idx]))));
221      }
222      else {
223        geo_stride[geo_track_idx] = 1;
224        geo_stride[geo_xtrack_idx] = 1;
225      }
226
227      int geo_track_start  = (int) Math.ceil((track_coords[0] - track_offset)/track_ratio);
228      int geo_xtrack_start = (int) Math.ceil((xtrack_coords[0] - xtrack_offset)/xtrack_ratio);
229
230      int geo_track_end  = (int) ((track_coords[1] - track_offset)/((double)track_ratio));
231      int geo_xtrack_end = (int) ((xtrack_coords[1] - xtrack_offset)/((double)xtrack_ratio));
232
233      geo_count[geo_track_idx]  = (int) ((geo_track_end - geo_track_start)/geo_stride[geo_track_idx]) + 1;
234      geo_count[geo_xtrack_idx] = (int) ((geo_xtrack_end - geo_xtrack_start)/geo_stride[geo_xtrack_idx]) + 1;
235
236      geo_track_end = geo_track_start + (geo_count[geo_track_idx]-1)*geo_stride[geo_track_idx];
237      geo_xtrack_end = geo_xtrack_start + (geo_count[geo_xtrack_idx]-1)*geo_stride[geo_xtrack_idx];
238     
239      geo_start[geo_track_idx]  = geo_track_start;
240      geo_start[geo_xtrack_idx] = geo_xtrack_start;
241
242      //-- convert back track/xtrack coords:
243      int new_track_start  = (int) (geo_track_start*track_ratio + (float)track_offset);
244      int new_xtrack_start = (int) (geo_xtrack_start*xtrack_ratio + (float)xtrack_offset);
245      int new_track_end  = (int) (geo_track_end*track_ratio + (float)track_offset);
246      int new_xtrack_end = (int) (geo_xtrack_end*xtrack_ratio + (float)xtrack_offset);
247
248
249      //- these must be only 2D (Swath dimensions)
250      double[] first = new double[2];
251      double[] last  = new double[2];
252      int[] length   = new int[2];
253
254      int track_idx;
255      int xtrack_idx;
256      if (geo_track_idx < geo_xtrack_idx) {
257        track_idx = 1;
258        xtrack_idx = 0;
259      } else {
260        track_idx = 0;
261        xtrack_idx = 1;
262      }
263
264      first[track_idx]  = new_track_start;
265      first[xtrack_idx] = new_xtrack_start;
266      last[track_idx]   = new_track_end;
267      last[xtrack_idx]  = new_xtrack_end;
268      length[track_idx]  = (int) ((last[track_idx] - first[track_idx])/stride[geo_track_idx] + 1);
269      length[xtrack_idx] = (int) ((last[xtrack_idx] - first[xtrack_idx])/stride[geo_xtrack_idx] + 1);
270
271      domainSet = new Linear2DSet(first[0], last[0], length[0], first[1], last[1], length[1]);
272   
273      Gridded2DSet gset = null;
274
275      gset = createInterpSet();
276
277      CoordinateSystem cs = new LongitudeLatitudeCoordinateSystem(domainSet, gset);
278
279      return cs;
280  }
281
282  Gridded2DSet createInterpSet() throws Exception {
283    Gridded2DSet gset = null;
284    if (type == DataType.FLOAT) {
285      float[] lonValues = reader.getFloatArray(lon_array_name, geo_start, geo_count, geo_stride);
286      float[] latValues = reader.getFloatArray(lat_array_name, geo_start, geo_count, geo_stride);
287                                                                                                                                             
288      gset = new Gridded2DSet(RealTupleType.SpatialEarth2DTuple,
289                     new float[][] {lonValues, latValues},
290                         geo_count[idx_order[0]], geo_count[idx_order[1]],
291                            null, null, null, false, false);
292    }
293    else if (type == DataType.DOUBLE) {
294      double[] lonValues = reader.getDoubleArray(lon_array_name, geo_start, geo_count, geo_stride);
295      double[] latValues = reader.getDoubleArray(lat_array_name, geo_start, geo_count, geo_stride);
296                                                                                                                                             
297      gset = new Gridded2DDoubleSet(RealTupleType.SpatialEarth2DTuple,
298                    new double[][] {lonValues, latValues},
299                       geo_count[idx_order[0]], geo_count[idx_order[1]],
300                           null, null, null, false);
301    }
302    else if (type == DataType.SHORT) {
303      short[] values = reader.getShortArray(lon_array_name, geo_start, geo_count, geo_stride);
304      Map<String, Object> metadata = new HashMap<>();
305      metadata.put(SwathAdapter.array_name, lon_array_name);
306      metadata.put(SwathAdapter.scale_name, scale_name);
307      metadata.put(SwathAdapter.offset_name, offset_name);
308      metadata.put(SwathAdapter.fill_value_name, fillValue_name);
309      RangeProcessor rangeProcessor = RangeProcessor.createRangeProcessor(reader, metadata);
310      float[] lonValues = rangeProcessor.processRange(values, null);
311      
312      values = reader.getShortArray(lat_array_name, geo_start, geo_count, geo_stride);
313      metadata = new HashMap<>();
314      metadata.put(SwathAdapter.array_name, lat_array_name);
315      metadata.put(SwathAdapter.scale_name, scale_name);
316      metadata.put(SwathAdapter.offset_name, offset_name);
317      metadata.put(SwathAdapter.fill_value_name, fillValue_name);
318      rangeProcessor = RangeProcessor.createRangeProcessor(reader, metadata);
319      float[] latValues = rangeProcessor.processRange(values, null);
320
321
322      gset = new Gridded2DSet(RealTupleType.SpatialEarth2DTuple,
323                     new float[][] {lonValues, latValues},
324                         geo_count[idx_order[0]], geo_count[idx_order[1]],
325                            null, null, null, false, false);
326
327    }
328    return gset;
329  }
330
331
332
333  public static Linear2DSet getNavigationDomain(double data_x_start, double data_x_stop, double data_x_stride,
334                                         double data_y_start, double data_y_stop, double data_y_stride,
335                                         double ratio_x, double ratio_y,
336                                         double offset_x, double offset_y,
337                                         int[] geo_start, int[] geo_count, int[] geo_stride)
338      throws Exception {
339
340      int geo_track_idx = 1;
341      int geo_xtrack_idx = 0;
342      double track_ratio = ratio_y;
343      double xtrack_ratio = ratio_x;
344      double track_offset = offset_y;
345      double xtrack_offset = offset_x;
346 
347      double[] track_coords = new double[3];
348      double[] xtrack_coords = new double[3];
349
350      xtrack_coords[0] = data_x_start;
351      xtrack_coords[1] = data_x_stop;
352      track_coords[0] = data_y_start;
353      track_coords[1] = data_y_stop;
354
355      double[] stride =  new double[2];
356      stride[geo_track_idx] = data_y_stride;
357      stride[geo_xtrack_idx] = data_x_stride;
358
359      if (track_ratio/(float)stride[0] <= 1) {
360        geo_stride[geo_track_idx] = (int) Math.round((1/(track_ratio/(stride[1]))));
361        geo_stride[geo_xtrack_idx] = (int) Math.round((1/(xtrack_ratio/(stride[0]))));
362      }
363      else {
364        geo_stride[0] = 1;
365        geo_stride[1] = 1;
366      }
367
368      int geo_track_start  = (int) Math.ceil((track_coords[0] - track_offset)/track_ratio);
369      int geo_xtrack_start = (int) Math.ceil((xtrack_coords[0] - xtrack_offset)/xtrack_ratio);
370
371      int geo_track_end  = (int) ((track_coords[1] - track_offset)/((double)track_ratio));
372      int geo_xtrack_end = (int) ((xtrack_coords[1] - xtrack_offset)/((double)xtrack_ratio));
373
374      geo_count[geo_track_idx]  = (int) ((geo_track_end - geo_track_start)/geo_stride[geo_track_idx]) + 1;
375      geo_count[geo_xtrack_idx] = (int) ((geo_xtrack_end - geo_xtrack_start)/geo_stride[geo_xtrack_idx]) + 1;
376
377      geo_track_end = geo_track_start + (geo_count[geo_track_idx]-1)*geo_stride[geo_track_idx];
378      geo_xtrack_end = geo_xtrack_start + (geo_count[geo_xtrack_idx]-1)*geo_stride[geo_xtrack_idx];
379
380      geo_start[geo_track_idx]  = geo_track_start;
381      geo_start[geo_xtrack_idx] = geo_xtrack_start;
382
383      //-- convert back track/xtrack coords:
384      int new_track_start  = (int) (geo_track_start*track_ratio + (float)track_offset);
385      int new_xtrack_start = (int) (geo_xtrack_start*xtrack_ratio + (float)xtrack_offset);
386      int new_track_end  = (int) (geo_track_end*track_ratio + (float)track_offset);
387      int new_xtrack_end = (int) (geo_xtrack_end*xtrack_ratio + (float)xtrack_offset);
388
389
390      double[] first = new double[2];
391      double[] last  = new double[2];
392      int[] length   = new int[2];
393      first[geo_track_idx]  = new_track_start;
394      first[geo_xtrack_idx] = new_xtrack_start;
395      last[geo_track_idx]   = new_track_end;
396      last[geo_xtrack_idx]  = new_xtrack_end;
397      length[geo_track_idx]  = (int) ((last[geo_track_idx] - first[geo_track_idx])/stride[geo_track_idx] + 1);
398      length[geo_xtrack_idx] = (int) ((last[geo_xtrack_idx] - first[geo_xtrack_idx])/stride[geo_xtrack_idx] + 1);
399
400      return new Linear2DSet(first[0], last[0], length[0], first[1], last[1], length[1]);
401
402  }
403
404
405}