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