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