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 visad.Data;
032import visad.FlatField;
033import visad.Set;
034import visad.Gridded1DSet;
035import visad.CoordinateSystem;
036import visad.RealType;
037import visad.RealTupleType;
038import visad.SetType;
039import visad.Linear1DSet;
040import visad.Linear2DSet;
041import visad.Unit;
042import visad.FunctionType;
043import visad.VisADException;
044import visad.QuickSort;
045import java.rmi.RemoteException;
046
047import java.util.Hashtable;
048import java.util.HashMap;
049import java.util.StringTokenizer;
050
051import java.io.BufferedReader;
052import java.io.FileInputStream;
053import java.io.IOException;
054import java.io.InputStream;
055import java.io.InputStreamReader;
056
057
058public abstract class ProfileAlongTrack extends MultiDimensionAdapter {
059
060      private FunctionType mathtype;
061
062      int TrackLen;
063      int VertLen;
064
065      private float[] vertLocs = null;
066      private float[] trackTimes = null;
067      private float[] trackLongitude = null;
068      private float[] trackLatitude = null;
069
070      public static String longitude_name = "Longitude";
071      public static String latitude_name  = "Latitude";
072      public static String trackDim_name  = "TrackDim";
073      public static String vertDim_name  = "VertDim";
074      public static String array_name = "array_name";
075      public static String profileTime_name = "ProfileTime";
076      public static String profileTime_unit = "ProfileTime_unit";
077      public static String altitude_unit = "altitude_unit";
078      public static String sfcElev_name = "SurfaceElev";
079      public static String range_name = "range_name";
080      public static String scale_name = "scale_name";
081      public static String offset_name = "offset_name";
082      public static String fill_value_name = "fill_value_name";
083      public static String valid_range = "valid_range";
084      public static String ancillary_file_name = "ancillary_file";
085      static String product_name = "product_name";
086      
087      String[] rangeName_s  = null;
088      Class[] arrayType_s = null;
089      Unit[] rangeUnit_s  = new Unit[] {null};
090
091      RealType track  = RealType.getRealType(trackDim_name);
092      RealType vert = RealType.getRealType(vertDim_name);
093      RealType[] domainRealTypes = new RealType[2];
094
095      RealType vertLocType;
096      RealType trackTimeType;
097
098      int track_idx      = -1;
099      int vert_idx       = -1;
100      int range_rank     = -1;
101
102      int track_tup_idx;
103      int vert_tup_idx;
104
105      boolean isVertDimAlt = true;
106
107      CoordinateSystem cs = null;
108
109      public static HashMap getEmptySubset() {
110        HashMap<String, double[]> subset = new HashMap<String, double[]>();
111        subset.put(trackDim_name, new double[3]);
112        subset.put(vertDim_name, new double[3]);
113        return subset;
114      }
115
116      public static HashMap getEmptyMetadataTable() {
117        HashMap<String, String> metadata = new HashMap<String, String>();
118        metadata.put(array_name, null);
119        metadata.put(trackDim_name, null);
120        metadata.put(vertDim_name, null);
121        metadata.put(longitude_name, null);
122        metadata.put(latitude_name, null);
123        metadata.put(profileTime_name, null);
124        metadata.put(profileTime_unit, null);
125        metadata.put(altitude_unit, null);
126        metadata.put(sfcElev_name, null);
127        metadata.put(scale_name, null);
128        metadata.put(offset_name, null);
129        metadata.put(fill_value_name, null);
130        /*
131        metadata.put(range_name, null);
132        metadata.put(range_unit, null);
133        metadata.put(valid_range, null);
134        */
135        return metadata;
136      }
137
138      public ProfileAlongTrack() {
139      }
140
141      public ProfileAlongTrack(MultiDimensionReader reader, HashMap metadata) {
142        this(reader, metadata, true);
143      }
144
145      public ProfileAlongTrack(MultiDimensionReader reader, HashMap metadata, boolean isVertDimAlt) {
146        super(reader, metadata);
147        this.isVertDimAlt = isVertDimAlt;
148        this.init();
149      }
150
151
152      private void init() {
153        for (int k=0; k<array_rank;k++) {
154          if ( ((String)metadata.get(trackDim_name)).equals(array_dim_names[k]) ) {
155            track_idx = k;
156          }
157          if ( ((String)metadata.get(vertDim_name)).equals(array_dim_names[k]) ) {
158            vert_idx = k;
159          }
160        }
161
162        int[] lengths = new int[2];
163
164        if (track_idx < vert_idx) {
165          domainRealTypes[0] = vert;
166          domainRealTypes[1] = track;
167          track_tup_idx = 1;
168          vert_tup_idx = 0;
169          lengths[0] = array_dim_lengths[vert_idx];
170          lengths[1] = array_dim_lengths[track_idx];
171        }
172        else {
173          domainRealTypes[0] = track;
174          domainRealTypes[1] = vert;
175          track_tup_idx = 0;
176          vert_tup_idx = 1;
177          lengths[0] = array_dim_lengths[track_idx];
178          lengths[1] = array_dim_lengths[vert_idx];
179        }
180
181        TrackLen = array_dim_lengths[track_idx];
182        VertLen = array_dim_lengths[vert_idx];
183
184        String rangeName = null;
185        if (metadata.get("range_name") != null) {
186          rangeName = (String)metadata.get("range_name");
187        } 
188        else {
189          rangeName = (String)metadata.get("array_name");
190        }
191        rangeType = RealType.getRealType(rangeName, rangeUnit_s[0]);
192
193        try {
194          rangeProcessor = RangeProcessor.createRangeProcessor(reader, metadata);
195        } 
196        catch (Exception e) {
197          System.out.println("RangeProcessor failed to create");
198          e.printStackTrace();
199        }
200
201        try {
202          if (isVertDimAlt) {
203            vertLocs = getVertBinAltitude();
204          }
205          vertLocType = makeVertLocType();
206          trackTimes = getTrackTimes();
207          trackTimeType = makeTrackTimeType();
208          trackLongitude = getTrackLongitude();
209          trackLatitude = getTrackLatitude();
210        } 
211        catch (Exception e) {
212          System.out.println(e);
213        }
214
215      }
216
217      public int getTrackLength() {
218        return TrackLen;
219      }
220
221      public int getVertLength() {
222        return VertLen;
223      }
224
225      public int getVertIdx() {
226        return vert_idx;
227      }
228
229      public int getTrackIdx() {
230        return track_idx;
231      }
232
233      public int getVertTupIdx() {
234        return vert_tup_idx;
235      }
236
237      public int getTrackTupIdx() {
238        return track_tup_idx;
239      }
240                                                                                                                                                     
241      public Set makeDomain(Object subset) throws Exception {
242        double[] first = new double[2];
243        double[] last = new double[2];
244        int[] length = new int[2];
245
246        HashMap<String, double[]> domainSubset = new HashMap<String, double[]>();
247        domainSubset.put(trackDim_name, (double[]) ((HashMap)subset).get(trackDim_name));
248        domainSubset.put(vertDim_name, (double[]) ((HashMap)subset).get(vertDim_name));
249
250        for (int kk=0; kk<2; kk++) {
251          RealType rtype = domainRealTypes[kk];
252          double[] coords = (double[]) ((HashMap)subset).get(rtype.getName());
253          first[kk] = coords[0];
254          last[kk] = coords[1];
255          length[kk] = (int) ((last[kk] - first[kk])/coords[2] + 1);
256          last[kk] = first[kk]+coords[2]*(length[kk]-1);
257        }
258        Linear2DSet domainSet = new Linear2DSet(first[0], last[0], length[0], first[1], last[1], length[1]);
259        final Linear1DSet[] lin1DSet_s = new Linear1DSet[] {domainSet.getLinear1DComponent(0),
260                                           domainSet.getLinear1DComponent(1)};
261
262        float[] new_altitudes = new float[length[vert_tup_idx]];
263        float[] new_times = new float[length[track_tup_idx]];
264
265        int track_start = (int) first[track_tup_idx];
266        int vert_start = (int) first[vert_tup_idx];
267        int vert_skip = (int) ((double[]) ((HashMap)subset).get(vertDim_name))[2];
268        int track_skip = (int) ((double[]) ((HashMap)subset).get(trackDim_name))[2];
269        for (int k=0; k<new_altitudes.length; k++) {
270          new_altitudes[k] = vertLocs[vert_start+(k*vert_skip)];
271        }
272
273        for (int k=0; k<new_times.length; k++) {
274          new_times[k] = trackTimes[track_start+(k*track_skip)];
275        }
276
277        final Gridded1DSet alt_set = new Gridded1DSet(vertLocType, new float[][] {new_altitudes}, new_altitudes.length);
278        final Gridded1DSet time_set = new Gridded1DSet(trackTimeType, new float[][] {new_times}, new_times.length);
279        final float vert_offset = (float) first[vert_tup_idx];
280        final float track_offset = (float) first[track_tup_idx];
281
282        RealTupleType reference = new RealTupleType(vertLocType, trackTimeType);
283        
284        CoordinateSystem cs = null;
285
286        try {
287        cs = new CoordinateSystem(reference, null) {
288          public float[][] toReference(float[][] vals) throws VisADException {
289            int[] indexes = lin1DSet_s[0].valueToIndex(new float[][] {vals[0]});
290            for (int k=0; k<vals[0].length;k++) {
291              //-indexes[k] = (int) (vals[vert_tup_idx][k] - vert_offset); ?
292            }
293            float[][] alts = alt_set.indexToValue(indexes);
294
295            indexes = lin1DSet_s[1].valueToIndex(new float[][] {vals[1]});
296            for (int k=0; k<vals[0].length;k++) {
297              //-indexes[k] = (int) (vals[track_tup_idx][k] - track_offset); ?
298            }
299            float[][] times = time_set.indexToValue(indexes);
300
301            return new float[][] {alts[0], times[0]};
302          }
303          public float[][] fromReference(float[][] vals) throws VisADException {
304            int[] indexes = alt_set.valueToIndex(new float[][] {vals[vert_tup_idx]});
305            float[][] vert_coords = lin1DSet_s[vert_tup_idx].indexToValue(indexes);
306            indexes = time_set.valueToIndex(new float[][] {vals[track_tup_idx]});
307            float[][] track_coords = lin1DSet_s[track_tup_idx].indexToValue(indexes);
308            return new float[][] {vert_coords[0], track_coords[0]};
309          }
310          public double[][] toReference(double[][] vals) throws VisADException {
311            return Set.floatToDouble(toReference(Set.doubleToFloat(vals)));
312          }
313          public double[][] fromReference(double[][] vals) throws VisADException {
314            return Set.floatToDouble(fromReference(Set.doubleToFloat(vals)));
315          }
316          public boolean equals(Object obj) {
317            return true;
318          }
319        };
320        }
321        catch (Exception e) {
322        }
323
324        RealTupleType domainTupType = new RealTupleType(domainRealTypes[0], domainRealTypes[1], cs, null);
325        domainSet = new Linear2DSet(domainTupType, first[0], last[0], length[0], first[1], last[1], length[1]);
326
327        return domainSet;
328      }
329
330      public FunctionType getMathType() {
331        return null;
332      }
333
334      public RealType[] getDomainRealTypes() {
335        return domainRealTypes;
336      }
337
338      public HashMap getDefaultSubset() {
339        HashMap subset = ProfileAlongTrack.getEmptySubset();
340
341        double[] coords = (double[])subset.get("TrackDim");
342        coords[0] = 20000.0;
343        coords[1] = (TrackLen - 15000.0) - 1;
344        //-coords[2] = 30.0;
345        coords[2] = 5.0;
346        subset.put("TrackDim", coords);
347
348        coords = (double[])subset.get("VertDim");
349        coords[0] = 98.0;
350        coords[1] = (VertLen) - 1;
351        coords[2] = 2.0;
352        subset.put("VertDim", coords);
353        return subset;
354      }
355
356      public int[] getTrackRangeInsideLonLatRect(double minLat, double maxLat, double minLon, double maxLon) {
357        int nn = 100;
358        int skip = TrackLen/nn;
359        double lon;
360        double lat;
361        
362        int idx = 0;
363        while (idx < TrackLen) {
364          lon = (double)trackLongitude[idx];
365          lat = (double)trackLatitude[idx];
366          if (((lon > minLon) && (lon < maxLon)) && ((lat > minLat)&&(lat < maxLat))) break;
367          idx += skip;
368        }
369        if (idx > TrackLen-1) idx = TrackLen-1;
370        if (idx == TrackLen-1) return new int[] {-1,-1};
371
372        int low_idx = idx;
373        while (low_idx > 0) {
374          lon = (double)trackLongitude[low_idx];
375          lat = (double)trackLatitude[low_idx];
376          if (((lon > minLon) && (lon < maxLon)) && ((lat > minLat)&&(lat < maxLat))) {
377            low_idx -= 1;
378            continue;
379          }
380          else {
381            break;
382          }
383        }
384
385        int hi_idx = idx;
386        while (hi_idx < TrackLen-1) {
387          lon = (double)trackLongitude[hi_idx];
388          lat = (double)trackLatitude[hi_idx];
389          if (((lon > minLon) && (lon < maxLon)) && ((lat > minLat)&&(lat < maxLat))) {
390            hi_idx += 1;
391            continue;
392          }
393          else {
394            break;
395          }
396        }
397        return new int[] {low_idx, hi_idx};
398      }
399
400      public HashMap getSubsetFromLonLatRect(HashMap subset, double minLat, double maxLat, double minLon, double maxLon) {
401        double[] coords = (double[])subset.get("TrackDim");
402        int[] idxs = getTrackRangeInsideLonLatRect(minLat, maxLat, minLon, maxLon);
403        coords[0] = (double) idxs[0];
404        coords[1] = (double) idxs[1];
405        if ((coords[0] == -1) || (coords[1] == -1)) return null;
406        return subset;
407      }
408
409      public HashMap getSubsetFromLonLatRect(HashMap subset, double minLat, double maxLat, double minLon, double maxLon,
410                                             int xStride, int yStride, int zStride) {
411
412        double[] coords = (double[])subset.get("TrackDim"); 
413        int[] idxs = getTrackRangeInsideLonLatRect(minLat, maxLat, minLon, maxLon);
414        coords[0] = (double) idxs[0];
415        coords[1] = (double) idxs[1];
416        if ((coords[0] == -1) || (coords[1] == -1)) return null;
417        if (xStride > 0) {
418          coords[2] = xStride;
419        }
420
421        coords = (double[])subset.get("VertDim");
422        if (yStride > 0) {
423          coords[2] = yStride;
424        }
425        return subset;
426      }
427
428      public HashMap getSubsetFromLonLatRect(double minLat, double maxLat, double minLon, double maxLon) {
429        return getSubsetFromLonLatRect(getDefaultSubset(), minLat, maxLat, minLon, maxLon);
430      }
431
432      public HashMap getSubsetFromLonLatRect(double minLat, double maxLat, double minLon, double maxLon,
433                                             int xStride, int yStride, int zStride) {
434
435        return getSubsetFromLonLatRect(getDefaultSubset(), minLat, maxLat, minLon, maxLon, xStride, yStride, zStride);
436      }
437
438      public abstract float[] getVertBinAltitude() throws Exception;
439      public abstract float[] getTrackTimes() throws Exception;
440      public abstract RealType makeVertLocType() throws Exception;
441      public abstract RealType makeTrackTimeType() throws Exception;
442      public abstract float[] getTrackLongitude() throws Exception;
443      public abstract float[] getTrackLatitude() throws Exception;
444
445      public static float[] medianFilter(float[] A, int lenx, int leny, int window_lenx, int window_leny)
446           throws VisADException {
447        float[] result =  new float[A.length];
448        float[] window =  new float[window_lenx*window_leny];
449        float[] new_window =  new float[window_lenx*window_leny];
450        int[] sort_indexes = new int[window_lenx*window_leny];
451                                                                                                                                    
452        int a_idx;
453        int w_idx;
454                                                                                                                                    
455        int w_lenx = window_lenx/2;
456        int w_leny = window_leny/2;
457                                                                                                                                    
458        int lo;
459        int hi;
460        int ww_jj;
461        int ww_ii;
462        int cnt;
463                                                                                                                                    
464        for (int j=0; j<leny; j++) {
465          for (int i=0; i<lenx; i++) {
466            a_idx = j*lenx + i;
467            cnt = 0;
468            for (int w_j=-w_leny; w_j<w_leny; w_j++) {
469              for (int w_i=-w_lenx; w_i<w_lenx; w_i++) {
470                ww_jj = w_j + j;
471                ww_ii = w_i + i;
472                w_idx = (w_j+w_leny)*window_lenx + (w_i+w_lenx);
473                if ((ww_jj >= 0) && (ww_ii >=0) && (ww_jj < leny) && (ww_ii < lenx)) {
474                  window[cnt] = A[ww_jj*lenx+ww_ii];
475                  cnt++;
476                }
477              }
478            }
479            System.arraycopy(window, 0, new_window, 0, cnt);
480            //-sort_indexes = QuickSort.sort(new_window, sort_indexes);
481            sort_indexes = QuickSort.sort(new_window);
482            result[a_idx] = new_window[cnt/2];
483          }
484        }
485        return result;
486      }
487
488}