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 */
028
029package edu.wisc.ssec.mcidasv.data.hydra;
030
031import java.util.ArrayList;
032import java.util.HashMap;
033import java.util.List;
034import java.util.Map;
035
036import org.slf4j.Logger;
037import org.slf4j.LoggerFactory;
038import visad.FunctionType;
039import visad.Gridded1DSet;
040import visad.QuickSort;
041import visad.RealTupleType;
042import visad.RealType;
043import visad.SampledSet;
044import visad.Set;
045import visad.SingletonSet;
046
047public class SpectrumAdapter extends MultiDimensionAdapter {
048  
049  private static final Logger logger =
050      LoggerFactory.getLogger(SpectrumAdapter.class);
051  
052  public static String channels_name = "Channels";
053  public static String channelIndex_name = "channelIndex";
054  public static String FOVindex_name = "FOVindex";
055  public static String channelUnit = "cm";
056  public static String channelType = "wavenumber";
057  public static String array_name  = "array_name";
058  public static String array_dimension_names = "array_dimension_names";
059  public static String range_name = "range_name";
060  public static String x_dim_name  = "x_dim"; //- 2 spatial dimensions, x fastest varying
061  public static String y_dim_name  = "y_dim"; //-----------------------------------------
062  public static String time_dim_name = "time_dim";
063  public static String ancillary_file_name = "ancillary_file";
064  public static String channelValues = "channelValues";
065  public static String bandNames = "bandNames";
066
067
068  public static Map<String, Object> getEmptyMetadataTable() {
069    Map<String, Object> metadata = new HashMap<>();
070    metadata.put(array_name, null);
071    metadata.put(range_name, null);
072    metadata.put(channelIndex_name, null);
073    metadata.put(ancillary_file_name, null);
074    metadata.put(x_dim_name, null);
075    metadata.put(y_dim_name, null);
076    metadata.put(time_dim_name, null);
077    metadata.put(channelUnit, null);
078    metadata.put(channelType, "wavenumber");
079    metadata.put(channelValues, null);
080    metadata.put(bandNames, null);
081
082    /*
083    metadata.put(scale_name, null);
084    metadata.put(offset_name, null);
085    metadata.put(fill_value_name, null);
086    metadata.put(range_unit, null);
087    metadata.put(valid_range, null);
088    */
089    return metadata;
090  }
091
092  public static Map<String, double[]> getEmptySubset() {
093    Map<String, double[]> subset = new HashMap<>();
094    subset.put(x_dim_name, new double[3]);
095    subset.put(y_dim_name, new double[3]);
096    subset.put(channelIndex_name, new double[3]);
097    return subset;
098  }
099
100  int numChannels;
101  int channelIndex = -1;
102  int[] channel_sort;
103  SampledSet domainSet;
104  RealType channelRealType;
105  RealType spectrumRangeType;
106  FunctionType spectrumType;
107
108  List<String> bandNameList = new ArrayList<>();
109  String[] bandNameArray = null;
110  Map<String, Float> bandNameMap = null;
111  boolean hasBandNames = false;
112
113  public SpectrumAdapter(MultiDimensionReader reader, Map<String, Object> metadata) {
114    super(reader, metadata);
115    this.init();
116  }
117
118  private void init() {
119    for (int k=0; k<array_rank;k++) {
120      String name = (String) metadata.get(channelIndex_name);
121      if (name != null) {
122        if ( name.equals(array_dim_names[k]) ) {
123          channelIndex = k;
124        }
125      }
126    }
127
128    numChannels = computeNumChannels();
129
130    String[] names = (String[]) metadata.get(bandNames);
131    if (names != null) {
132      hasBandNames = true;
133      bandNameArray = new String[names.length];
134      for (int k=0; k<names.length;k++) {
135        bandNameList.add(names[k]);
136        bandNameArray[k] = names[k];
137      }
138    }
139
140    try {
141      domainSet = makeDomainSet();
142      rangeType = makeSpectrumRangeType();
143      spectrumType = new FunctionType(channelRealType, spectrumRangeType);
144    } catch (Exception e) {
145      logger.error("Cannot create spectrum domain", e);
146    }
147  
148  }
149
150  public boolean hasBandNames() {
151     return hasBandNames;
152  }
153
154  public List<String> getBandNames() {
155    return bandNameList;
156  }
157
158  public Map<String, Float> getBandNameMap() {
159    return bandNameMap;
160  }
161
162  public int computeNumChannels() {
163    if (channelIndex == -1) {
164      return 1;
165    } 
166    else {
167      return array_dim_lengths[channelIndex];
168    }
169  }
170
171  public Set makeDomain(Map<String, double[]> subset) throws Exception {
172    return domainSet;
173  }
174
175  public SampledSet getDomainSet() throws Exception {
176    return domainSet;
177  }
178
179  private SampledSet makeDomainSet() throws Exception {
180    RealType domainType = makeSpectrumDomainType();
181    float[] channels = getChannels();
182    channel_sort = QuickSort.sort(channels);
183    if (numChannels == 1) {
184      domainSet = new SingletonSet(new RealTupleType(domainType), new double[] {(double)channels[0]}, null, null, null);
185    }
186    else {
187      domainSet = new Gridded1DSet(domainType, new float[][] {channels}, numChannels);
188    }
189    return domainSet;
190  }
191
192  public float[] getChannels() throws Exception {
193    float[] channels = null;
194    if (metadata.get(channelValues) == null) {
195      channels = reader.getFloatArray((String)metadata.get(channels_name),
196                                            new int[] {0}, new int[] {numChannels}, new int[] {1});
197    } 
198    else {
199      channels = (float[]) metadata.get(channelValues);
200    }
201
202    if (hasBandNames) {
203      bandNameMap = new HashMap<>();
204      for (int k=0; k<numChannels; k++) {
205        bandNameMap.put(bandNameArray[k], new Float(channels[k]));
206      }
207    }
208    return channels;
209  }
210
211  public RealType makeSpectrumDomainType() throws Exception {
212    /**
213    if ( ((String)metadata.get(channelType)).equals("wavenumber") ) {
214      ScaledUnit centimeter = new ScaledUnit(0.01, CommonUnit.meter, "cm");
215      Unit tmp_unit = centimeter.pow(-1);
216      ScaledUnit inv_centimeter = new ScaledUnit(1.0, tmp_unit, "cm^-1");
217      channelRealType = RealType.getRealType("wavenumber", null);
218    }
219    **/
220    channelRealType = RealType.getRealType((String)metadata.get(channelType), null);
221    return channelRealType;
222  }
223
224  public RealType makeSpectrumRangeType() throws Exception {
225    spectrumRangeType = RealType.getRealType("Radiance");
226    return spectrumRangeType;
227  }
228
229  float[] sortRange(float[] range) {
230    float[] sorted_range = new float[numChannels];
231    for (int k=0; k<numChannels; k++) sorted_range[k] = range[channel_sort[k]];
232    return sorted_range;
233  }
234
235  double[] sortRange(double[] range) {
236    double[] sorted_range =  new double[numChannels];
237    for (int k=0; k<numChannels; k++) sorted_range[k] = range[channel_sort[k]];
238    return sorted_range;
239  }
240
241  public Map<String, double[]> getDefaultSubset() {
242    Map<String, double[]> subset = SpectrumAdapter.getEmptySubset();
243    
244    double[] coords = (double[])subset.get(y_dim_name);
245    coords[0] = 1.0;
246    coords[1] = 1.0;
247    coords[2] = 1.0;
248    subset.put(y_dim_name, coords);
249                                                                                                                                     
250    coords = (double[])subset.get(x_dim_name);
251    coords[0] = 1.0;
252    coords[1] = 1.0;
253    coords[2] = 1.0;
254    subset.put(x_dim_name, coords);
255
256    coords = (double[])subset.get(channelIndex_name);
257    coords[0] = 0.0;
258    coords[1] = (double) (numChannels - 1);
259    coords[2] = 1.0;
260    subset.put(channelIndex_name, coords);
261
262    return subset;
263  }
264
265  public int getChannelIndexFromWavenumber(float wavenumber) throws Exception {
266    int idx = (domainSet.valueToIndex(new float[][] {{wavenumber}}))[0];
267    return channel_sort[idx];
268  }
269
270  public float getWavenumberFromChannelIndex(int index) throws Exception {
271    int idx = channel_sort[index];
272    return (domainSet.indexToValue(new int[] {idx}))[0][0];
273  }
274
275  public int getNumChannels() {
276    return numChannels;
277  }
278}