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