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