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