001/*
002 * This file is part of McIDAS-V
003 *
004 * Copyright 2007-2023
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 http://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 visad.CoordinateSystem;
036import visad.Gridded2DDoubleSet;
037import visad.Gridded2DSet;
038import visad.Linear2DSet;
039import visad.RealTupleType;
040
041public class SwathNavigation implements Navigation  {
042
043  public static SwathNavigation createNavigation(SwathAdapter swathAdapter) throws Exception {
044    String product_name = null;
045    SwathNavigation swathNav = null;
046    
047    product_name = (String)swathAdapter.getMetadata().get(SwathAdapter.product_name);
048
049    if (product_name == null) {
050      swathNav = new SwathNavigation(swathAdapter);
051    }
052    else if (Objects.equals(product_name, "IASI_L1C_xxx")) {
053      swathNav = new IASI_L1C_LonLatNavigation(swathAdapter);
054    }
055    else if (Objects.equals(product_name, "CrIS_SDR")) {
056      swathNav = new CrIS_SDR_LonLatNavigation(swathAdapter);
057    }
058    else {
059      swathNav = new SwathNavigation(swathAdapter);
060    }
061    
062    return swathNav;
063  }
064
065  int geo_track_idx;
066  int geo_xtrack_idx;
067  int geoTrackLen;
068  int geoXTrackLen;
069
070  SwathAdapter swathAdapter;
071  MultiDimensionReader reader;
072  String lon_array_name;
073  String lat_array_name;
074  int[] idx_order = new int[2];
075  float ratio = 1;
076  float track_ratio = 1;
077  float xtrack_ratio = 1;
078  double track_offset = 0;
079  double xtrack_offset = 0;
080  int track_idx;
081  int xtrack_idx;
082  int[] geo_stride = new int[2];
083  int[] geo_count = new int[2];
084  int[] geo_start = new int[2];
085
086  String scale_name = "SCALE_NAME";
087  String offset_name = "OFFSET_NAME";
088  String fillValue_name = "_FILLVALUE";
089
090  int numDims = 2;
091
092  Class type;
093
094  public SwathNavigation(SwathAdapter swathAdapter) throws Exception {
095
096    Map<String, Object> metadata = swathAdapter.getMetadata();
097    reader = swathAdapter.getReader();
098    this.swathAdapter = swathAdapter;
099    track_idx = swathAdapter.track_idx;
100    xtrack_idx = swathAdapter.xtrack_idx;
101
102    lon_array_name = (String)metadata.get(SwathAdapter.lon_array_name);
103    lat_array_name = (String)metadata.get(SwathAdapter.lat_array_name);
104
105    String[] lon_dim_names = null;
106
107    String[] lonDimNames = (String[])metadata.get(SwathAdapter.lon_array_dimension_names);
108
109    if (lonDimNames != null) {
110      lon_dim_names = lonDimNames;
111    }
112    else {
113      lon_dim_names = reader.getDimensionNames(lon_array_name);
114    }
115
116    int[] lon_dim_lengths = reader.getDimensionLengths(lon_array_name);
117
118    numDims = lon_dim_lengths.length;
119    geo_stride = new int[numDims];
120    geo_count = new int[numDims];
121    geo_start = new int[numDims];
122
123
124    String geo_track_name = (String)metadata.get(SwathAdapter.geo_track_name);
125    String geo_xtrack_name = (String)metadata.get(SwathAdapter.geo_xtrack_name);
126
127    for (int k=0; k<numDims;k++) {
128      if ( geo_track_name.equals(lon_dim_names[k]) ) {
129         geo_track_idx = k;
130      }
131      if ( geo_xtrack_name.equals(lon_dim_names[k]) ) {
132         geo_xtrack_idx = k;
133      }
134    }
135
136    if (geo_track_idx < geo_xtrack_idx) {
137      idx_order[0] = geo_xtrack_idx;
138      idx_order[1] = geo_track_idx;
139    }
140    else {
141      idx_order[0] = geo_track_idx;
142      idx_order[1] = geo_xtrack_idx;
143    }
144
145    geoTrackLen  = lon_dim_lengths[geo_track_idx];
146    geoXTrackLen = lon_dim_lengths[geo_xtrack_idx];
147
148    String str = (String)metadata.get(SwathAdapter.geo_track_skip_name);
149
150    if (str != null) {
151      track_ratio = (float) Double.parseDouble(str);
152      ratio = track_ratio;
153    }
154    str = (String)metadata.get(SwathAdapter.geo_xtrack_skip_name);
155    if (str != null) {
156      xtrack_ratio = (float) Double.parseDouble(str);
157    }
158    str = (String)metadata.get(SwathAdapter.geo_track_offset_name);
159    if (str != null) {
160      track_offset = Double.parseDouble(str);
161    }
162    str = (String)metadata.get(SwathAdapter.geo_xtrack_offset_name);
163    if (str != null) {
164      xtrack_offset = Double.parseDouble(str);
165    }
166
167    str = (String)metadata.get(SwathAdapter.geo_scale_name);
168    if (str != null) {
169      scale_name = str;
170    }
171
172    str = (String)metadata.get(SwathAdapter.geo_offset_name);
173    if (str != null) {
174      offset_name = str;
175    }
176
177    str = (String)metadata.get(SwathAdapter.geo_fillValue_name);
178    if (str != null) {
179      fillValue_name = str;
180    }
181 
182    type = reader.getArrayType(lon_array_name);
183  }
184
185  public CoordinateSystem getVisADCoordinateSystem(Linear2DSet domainSet, Map<String, double[]> domainSubset) throws Exception
186  {
187      Subset select = swathAdapter.getIndexes(domainSubset);
188
189      double[] track_coords = domainSubset.get(SwathAdapter.track_name);
190      double[] xtrack_coords = domainSubset.get(SwathAdapter.xtrack_name);
191      
192      int[] stride = new int[numDims];
193      stride[geo_track_idx] = (int) track_coords[2];
194      stride[geo_xtrack_idx] = (int) xtrack_coords[2]; 
195
196
197      if (numDims > 2) { // initialize geo arrays, then recompute xtrack/track dimensions below
198        if (numDims == select.getRank()) {
199          int[] start = select.getStart();
200          int[] count = select.getCount();
201          stride = select.getStride();
202          for (int i=0; i<numDims; i++) {
203            geo_start[i] = start[i];
204            geo_count[i] = count[i];
205            geo_stride[i] = stride[i];
206          }
207        }
208        else {
209          geo_start[geo_track_idx] = (int) track_coords[0];
210          geo_start[geo_xtrack_idx] = (int) xtrack_coords[0];
211          geo_count[geo_track_idx] = (int) ((track_coords[1] - track_coords[0])/track_coords[2] + 1f);
212          geo_count[geo_xtrack_idx] = (int) ((xtrack_coords[1] - xtrack_coords[0])/xtrack_coords[2] + 1f);
213        }
214      }
215
216
217      if (ratio/(float)stride[0] <= 1) {
218        geo_stride[geo_track_idx] = Math.round((1f/(track_ratio/((float)stride[geo_track_idx]))));
219        geo_stride[geo_xtrack_idx] = Math.round((1f/(xtrack_ratio/((float)stride[geo_xtrack_idx]))));
220      }
221      else {
222        geo_stride[geo_track_idx] = 1;
223        geo_stride[geo_xtrack_idx] = 1;
224      }
225
226      int geo_track_start  = (int) Math.ceil((track_coords[0] - track_offset)/track_ratio);
227      int geo_xtrack_start = (int) Math.ceil((xtrack_coords[0] - xtrack_offset)/xtrack_ratio);
228
229      int geo_track_end  = (int) ((track_coords[1] - track_offset)/((double)track_ratio));
230      int geo_xtrack_end = (int) ((xtrack_coords[1] - xtrack_offset)/((double)xtrack_ratio));
231
232      geo_count[geo_track_idx]  = (int) ((geo_track_end - geo_track_start)/geo_stride[geo_track_idx]) + 1;
233      geo_count[geo_xtrack_idx] = (int) ((geo_xtrack_end - geo_xtrack_start)/geo_stride[geo_xtrack_idx]) + 1;
234
235      geo_track_end = geo_track_start + (geo_count[geo_track_idx]-1)*geo_stride[geo_track_idx];
236      geo_xtrack_end = geo_xtrack_start + (geo_count[geo_xtrack_idx]-1)*geo_stride[geo_xtrack_idx];
237     
238      geo_start[geo_track_idx]  = geo_track_start;
239      geo_start[geo_xtrack_idx] = geo_xtrack_start;
240
241      //-- convert back track/xtrack coords:
242      int new_track_start  = (int) (geo_track_start*track_ratio + (float)track_offset);
243      int new_xtrack_start = (int) (geo_xtrack_start*xtrack_ratio + (float)xtrack_offset);
244      int new_track_end  = (int) (geo_track_end*track_ratio + (float)track_offset);
245      int new_xtrack_end = (int) (geo_xtrack_end*xtrack_ratio + (float)xtrack_offset);
246
247
248      //- these must be only 2D (Swath dimensions)
249      double[] first = new double[2];
250      double[] last  = new double[2];
251      int[] length   = new int[2];
252
253      int track_idx;
254      int xtrack_idx;
255      if (geo_track_idx < geo_xtrack_idx) {
256        track_idx = 1;
257        xtrack_idx = 0;
258      } else {
259        track_idx = 0;
260        xtrack_idx = 1;
261      }
262
263      first[track_idx]  = new_track_start;
264      first[xtrack_idx] = new_xtrack_start;
265      last[track_idx]   = new_track_end;
266      last[xtrack_idx]  = new_xtrack_end;
267      length[track_idx]  = (int) ((last[track_idx] - first[track_idx])/stride[geo_track_idx] + 1);
268      length[xtrack_idx] = (int) ((last[xtrack_idx] - first[xtrack_idx])/stride[geo_xtrack_idx] + 1);
269
270      domainSet = new Linear2DSet(first[0], last[0], length[0], first[1], last[1], length[1]);
271   
272      Gridded2DSet gset = null;
273
274      gset = createInterpSet();
275
276      CoordinateSystem cs = new LongitudeLatitudeCoordinateSystem(domainSet, gset);
277
278      return cs;
279  }
280
281  Gridded2DSet createInterpSet() throws Exception {
282    Gridded2DSet gset = null;
283    if (type == Float.TYPE) {
284      float[] lonValues = reader.getFloatArray(lon_array_name, geo_start, geo_count, geo_stride);
285      float[] latValues = reader.getFloatArray(lat_array_name, geo_start, geo_count, geo_stride);
286                                                                                                                                             
287      gset = new Gridded2DSet(RealTupleType.SpatialEarth2DTuple,
288                     new float[][] {lonValues, latValues},
289                         geo_count[idx_order[0]], geo_count[idx_order[1]],
290                            null, null, null, false, false);
291    }
292    else if (type == Double.TYPE) {
293      double[] lonValues = reader.getDoubleArray(lon_array_name, geo_start, geo_count, geo_stride);
294      double[] latValues = reader.getDoubleArray(lat_array_name, geo_start, geo_count, geo_stride);
295                                                                                                                                             
296      gset = new Gridded2DDoubleSet(RealTupleType.SpatialEarth2DTuple,
297                    new double[][] {lonValues, latValues},
298                       geo_count[idx_order[0]], geo_count[idx_order[1]],
299                           null, null, null, false);
300    }
301    else if (type == Short.TYPE) {
302      short[] values = reader.getShortArray(lon_array_name, geo_start, geo_count, geo_stride);
303      Map<String, Object> metadata = new HashMap<>();
304      metadata.put(SwathAdapter.array_name, lon_array_name);
305      metadata.put(SwathAdapter.scale_name, scale_name);
306      metadata.put(SwathAdapter.offset_name, offset_name);
307      metadata.put(SwathAdapter.fill_value_name, fillValue_name);
308      RangeProcessor rangeProcessor = RangeProcessor.createRangeProcessor(reader, metadata);
309      float[] lonValues = rangeProcessor.processRange(values, null);
310      
311      values = reader.getShortArray(lat_array_name, geo_start, geo_count, geo_stride);
312      metadata = new HashMap<>();
313      metadata.put(SwathAdapter.array_name, lat_array_name);
314      metadata.put(SwathAdapter.scale_name, scale_name);
315      metadata.put(SwathAdapter.offset_name, offset_name);
316      metadata.put(SwathAdapter.fill_value_name, fillValue_name);
317      rangeProcessor = RangeProcessor.createRangeProcessor(reader, metadata);
318      float[] latValues = rangeProcessor.processRange(values, null);
319
320
321      gset = new Gridded2DSet(RealTupleType.SpatialEarth2DTuple,
322                     new float[][] {lonValues, latValues},
323                         geo_count[idx_order[0]], geo_count[idx_order[1]],
324                            null, null, null, false, false);
325
326    }
327    return gset;
328  }
329
330
331
332  public static Linear2DSet getNavigationDomain(double data_x_start, double data_x_stop, double data_x_stride,
333                                         double data_y_start, double data_y_stop, double data_y_stride,
334                                         double ratio_x, double ratio_y,
335                                         double offset_x, double offset_y,
336                                         int[] geo_start, int[] geo_count, int[] geo_stride)
337      throws Exception {
338
339      int geo_track_idx = 1;
340      int geo_xtrack_idx = 0;
341      double track_ratio = ratio_y;
342      double xtrack_ratio = ratio_x;
343      double track_offset = offset_y;
344      double xtrack_offset = offset_x;
345 
346      double[] track_coords = new double[3];
347      double[] xtrack_coords = new double[3];
348
349      xtrack_coords[0] = data_x_start;
350      xtrack_coords[1] = data_x_stop;
351      track_coords[0] = data_y_start;
352      track_coords[1] = data_y_stop;
353
354      double[] stride =  new double[2];
355      stride[geo_track_idx] = data_y_stride;
356      stride[geo_xtrack_idx] = data_x_stride;
357
358      if (track_ratio/(float)stride[0] <= 1) {
359        geo_stride[geo_track_idx] = (int) Math.round((1/(track_ratio/(stride[1]))));
360        geo_stride[geo_xtrack_idx] = (int) Math.round((1/(xtrack_ratio/(stride[0]))));
361      }
362      else {
363        geo_stride[0] = 1;
364        geo_stride[1] = 1;
365      }
366
367      int geo_track_start  = (int) Math.ceil((track_coords[0] - track_offset)/track_ratio);
368      int geo_xtrack_start = (int) Math.ceil((xtrack_coords[0] - xtrack_offset)/xtrack_ratio);
369
370      int geo_track_end  = (int) ((track_coords[1] - track_offset)/((double)track_ratio));
371      int geo_xtrack_end = (int) ((xtrack_coords[1] - xtrack_offset)/((double)xtrack_ratio));
372
373      geo_count[geo_track_idx]  = (int) ((geo_track_end - geo_track_start)/geo_stride[geo_track_idx]) + 1;
374      geo_count[geo_xtrack_idx] = (int) ((geo_xtrack_end - geo_xtrack_start)/geo_stride[geo_xtrack_idx]) + 1;
375
376      geo_track_end = geo_track_start + (geo_count[geo_track_idx]-1)*geo_stride[geo_track_idx];
377      geo_xtrack_end = geo_xtrack_start + (geo_count[geo_xtrack_idx]-1)*geo_stride[geo_xtrack_idx];
378
379      geo_start[geo_track_idx]  = geo_track_start;
380      geo_start[geo_xtrack_idx] = geo_xtrack_start;
381
382      //-- convert back track/xtrack coords:
383      int new_track_start  = (int) (geo_track_start*track_ratio + (float)track_offset);
384      int new_xtrack_start = (int) (geo_xtrack_start*xtrack_ratio + (float)xtrack_offset);
385      int new_track_end  = (int) (geo_track_end*track_ratio + (float)track_offset);
386      int new_xtrack_end = (int) (geo_xtrack_end*xtrack_ratio + (float)xtrack_offset);
387
388
389      double[] first = new double[2];
390      double[] last  = new double[2];
391      int[] length   = new int[2];
392      first[geo_track_idx]  = new_track_start;
393      first[geo_xtrack_idx] = new_xtrack_start;
394      last[geo_track_idx]   = new_track_end;
395      last[geo_xtrack_idx]  = new_xtrack_end;
396      length[geo_track_idx]  = (int) ((last[geo_track_idx] - first[geo_track_idx])/stride[geo_track_idx] + 1);
397      length[geo_xtrack_idx] = (int) ((last[geo_xtrack_idx] - first[geo_xtrack_idx])/stride[geo_xtrack_idx] + 1);
398
399      return new Linear2DSet(first[0], last[0], length[0], first[1], last[1], length[1]);
400
401  }
402
403
404}