001/*
002 * This file is part of McIDAS-V
003 *
004 * Copyright 2007-2016
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 */
028package edu.wisc.ssec.mcidasv.data;
029
030
031import edu.wisc.ssec.mcidasv.data.hydra.NetCDFFile;
032import visad.Data;
033import visad.Unit;
034import visad.Gridded3DSet;
035import visad.Gridded1DDoubleSet;
036import visad.Gridded1DSet;
037import visad.Set;
038import visad.UnionSet;
039import visad.RealTupleType;
040import visad.RealType;
041import visad.FlatField;
042import visad.FieldImpl;
043import visad.Integer1DSet;
044import visad.FunctionType;
045import visad.CoordinateSystem;
046import visad.VisADException;
047import visad.DateTime;
048import visad.data.units.Parser;
049import visad.data.units.ParseException;
050
051import java.rmi.RemoteException;
052import java.util.Hashtable;
053import java.util.List;
054
055import ucar.unidata.util.Misc;
056import ucar.unidata.util.Range;
057
058import ucar.unidata.data.DataUtil;
059import ucar.unidata.data.DataChoice;
060import ucar.unidata.data.DataDataChoice;
061import ucar.unidata.data.DirectDataChoice;
062import ucar.unidata.data.DataSelection;
063import ucar.unidata.data.DataCategory;
064import ucar.unidata.data.DataSourceImpl;
065import ucar.unidata.data.DataSourceDescriptor;
066
067
068
069public class NearCastTrajDataSource extends DataSourceImpl {
070
071    private static final String DATA_DESCRIPTION = "NearCastTrajectory";
072
073    public static String parcelDimName = "parcel";
074    public static String timeDimName = "times";
075    public static String lonName = "lon";
076    public static String latName = "lat";
077
078    NetCDFFile ncFile = null;
079    String pressName = "pres";
080    String timeName = "times";
081
082
083    int[] start = new int[2];
084    int[] count = new int[2];
085    int[] stride = new int[2];
086
087
088    int parcelDimIdx = 0;
089    int timeDimIdx = 1;
090
091    String[] paramList = null;
092    
093    String fileName = null;
094
095    CoordinateSystem presToHeightCS = null;
096
097    Unit timeUnit;
098    Set timeSet;
099
100    int numTimes;
101    int numParcels;
102
103    Range lonRange = new Range();
104    Range latRange = new Range();
105    Range paramRange = new Range();
106
107    public NearCastTrajDataSource() {
108    }
109
110    public NearCastTrajDataSource(String filename) throws VisADException {
111      this(null, Misc.newList(filename), null);
112    }
113
114    public NearCastTrajDataSource(DataSourceDescriptor descriptor,
115                                 String fileName, Hashtable properties) throws VisADException { 
116        this(descriptor, Misc.newList(fileName), properties);
117    }
118
119    public NearCastTrajDataSource(DataSourceDescriptor descriptor,
120                                 List newSources, Hashtable properties) throws VisADException {
121
122       super(descriptor, DATA_DESCRIPTION, DATA_DESCRIPTION, properties);
123
124       presToHeightCS = DataUtil.getPressureToHeightCS(DataUtil.STD_ATMOSPHERE);
125
126       fileName = (String) newSources.get(0);
127
128       try {
129         ncFile = new NetCDFFile(fileName);
130       }
131       catch (Exception e) {
132         e.printStackTrace();
133       }
134         
135       paramList = new String[] {"temp", "q", "the", "tp", "MS", "MQ", "MTe", "TP"};
136
137
138       String unitStr = ncFile.getArrayUnitString(timeName);
139       try {
140         timeUnit = Parser.parse(unitStr);
141       } 
142       catch (ParseException e) {
143         System.out.println(e);
144       }
145
146       numTimes = ncFile.getDimensionLength(timeDimName);
147       numParcels = ncFile.getDimensionLength(parcelDimName);
148
149       try {
150         Class type = ncFile.getArrayType(timeName);
151         if (type == Double.TYPE) {
152            double[] timeValues = ncFile.getDoubleArray(timeName, new int[] {0}, new int[] {numTimes}, new int[] {1});
153            timeSet = new Gridded1DDoubleSet(
154                       RealType.Time, new double[][] {timeValues}, numTimes, null, new Unit[] {timeUnit}, null);
155         }
156         else if (type == Float.TYPE) {
157            float[] timeValues = ncFile.getFloatArray(timeName, new int[] {0}, new int[] {numTimes}, new int[] {1});
158            timeSet = new Gridded1DSet(RealType.Time, new float[][] {timeValues}, numTimes, null, new Unit[] {timeUnit}, null);
159
160         }
161       } 
162       catch (Exception e) {
163         e.printStackTrace();
164       }
165    }
166
167
168    public FlatField[] createVisADData(String paramName) {
169      return null;
170    }
171
172    public FlatField singleTraj(String paramName, int parcelIndex, int timeStart, int timeCount, int timeStride) throws Exception {
173
174      start[parcelDimIdx] = parcelIndex;
175      start[timeDimIdx] = timeStart;
176
177      count[parcelDimIdx] = 1;
178      count[timeDimIdx] = timeCount;
179
180      stride[parcelDimIdx] = 1;
181      stride[timeDimIdx] = 1;
182
183      float[] lons = ncFile.getFloatArray(lonName, start, count, stride);
184      float[] lats = ncFile.getFloatArray(latName, start, count, stride);
185
186      float[] minmax = minmax(lons, (float)lonRange.getMin(), (float)lonRange.getMax());
187      lonRange.setMin(minmax[0]);
188      lonRange.setMax(minmax[1]);
189      minmax = minmax(lats, (float)latRange.getMin(), (float)latRange.getMax());
190      latRange.setMin(minmax[0]);
191      latRange.setMax(minmax[1]);
192
193      float[] pres = ncFile.getFloatArray(pressName, start, count, stride);
194      float[] param = ncFile.getFloatArray(paramName, start, count, stride);
195      minmax = minmax(param, (float)paramRange.getMin(), (float)paramRange.getMax());
196      paramRange.setMin(minmax[0]);
197      paramRange.setMax(minmax[1]);
198
199      float[] alt = (presToHeightCS.toReference(new float[][] {pres}))[0];
200 
201      float[][] trajCoords = new float[][] {lons, lats, alt};
202
203      Gridded3DSet domain = new Gridded3DSet(RealTupleType.SpatialEarth3DTuple, trajCoords, trajCoords[0].length);
204
205      FunctionType fncType = new FunctionType(RealTupleType.SpatialEarth3DTuple, RealType.getRealType(paramName));
206      FlatField traj = new FlatField(fncType, domain);
207      traj.setSamples(new float[][] {param}, false);
208 
209      return traj;
210    }
211
212    /**
213     * Make and insert the {@link DataChoice DataChoices} for this {@code DataSource}.
214     */
215    public void doMakeDataChoices() {
216        try {
217            for (int k=0; k<paramList.length; k++) {
218                DataChoice choice = doMakeDataChoice(k);
219                if (choice != null) {
220                    addDataChoice(choice);
221                }
222            }
223        }
224        catch(Exception e) {
225            e.printStackTrace();
226        }
227    }
228
229    private DataChoice doMakeDataChoice(int idx) throws Exception {
230        String name = paramList[idx];
231        DirectDataChoice ddc = null;
232        if (ncFile.hasArray(name)) {
233            ddc = new DirectDataChoice(this, new Integer(idx), name, name, null, new Hashtable());
234        }
235        return ddc;
236    }
237
238    protected Data getDataInner(DataChoice dataChoice, DataCategory category,
239                                DataSelection dataSelection,
240                                Hashtable requestProperties)
241            throws VisADException, RemoteException {
242
243        String paramName = dataChoice.getName();
244
245        FieldImpl trajField = new FieldImpl(
246                      new FunctionType(RealType.Generic, new FunctionType(RealTupleType.SpatialEarth3DTuple, RealType.getRealType(paramName))), new Integer1DSet(numParcels));
247
248        FieldImpl trajTimeField = new FieldImpl(new FunctionType(RealType.Time, trajField.getType()), timeSet); 
249
250        lonRange.setMin(Float.MAX_VALUE);
251        lonRange.setMax(-Float.MAX_VALUE);
252        latRange.setMin(Float.MAX_VALUE);
253        latRange.setMax(-Float.MAX_VALUE);
254        paramRange.setMin(Float.MAX_VALUE);
255        paramRange.setMax(-Float.MAX_VALUE);
256
257        try {
258          for (int t=0; t<numTimes; t++) {
259             trajField = new FieldImpl(
260                      new FunctionType(RealType.Generic, new FunctionType(RealTupleType.SpatialEarth3DTuple, RealType.getRealType(paramName))), new Integer1DSet(numParcels));
261            for (int k=0; k<numParcels/4; k++) {
262               FlatField fld = singleTraj(paramName, k*4, 0, t+1, 1);
263               trajField.setSample(k, fld);
264            }
265            trajTimeField.setSample(t, trajField);
266          }
267          return trajTimeField;
268        }
269        catch (Exception e) {
270          e.printStackTrace();
271          return null;
272        }
273    }
274
275    public static float[] minmax(float[] values, float min, float max) {
276      for (int k = 0; k < values.length; k++) {
277        float val = values[k];
278        if ((val == val) && (val < Float.POSITIVE_INFINITY) && (val > Float.NEGATIVE_INFINITY)) {
279          if (val < min) min = val;
280          if (val > max) max = val;
281        }
282      }
283      return new float[] {min, max};
284    }
285
286   public Range getLonRange() {
287     return lonRange;
288   }
289
290   public Range getLatRange() {
291     return latRange;
292   }
293
294   public Range getParamRange() {
295     return paramRange;
296   }
297
298}