001/* 002 * This file is part of McIDAS-V 003 * 004 * Copyright 2007-2025 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, Integer.valueOf(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}