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.hrit; 030 031import java.awt.BorderLayout; 032import java.awt.FlowLayout; 033import java.io.File; 034import java.io.IOException; 035import java.rmi.RemoteException; 036import java.util.ArrayList; 037import java.util.Hashtable; 038import java.util.List; 039 040import javax.swing.JComboBox; 041import javax.swing.JComponent; 042import javax.swing.JPanel; 043 044import edu.wisc.ssec.mcidas.Calibrator; 045 046import org.slf4j.Logger; 047import org.slf4j.LoggerFactory; 048import visad.Data; 049import visad.VisADException; 050import visad.data.hrit.HRITAdapter; 051 052import ucar.unidata.data.DataCategory; 053import ucar.unidata.data.DataChoice; 054import ucar.unidata.data.DataSelection; 055import ucar.unidata.data.DataSelectionComponent; 056import ucar.unidata.data.DataSourceDescriptor; 057import ucar.unidata.data.DataSourceImpl; 058import ucar.unidata.data.DirectDataChoice; 059import ucar.unidata.util.Misc; 060import ucar.unidata.util.WrapperException; 061 062public class HRITDataSource extends DataSourceImpl { 063 064 private static final Logger logger = 065 LoggerFactory.getLogger(HRITDataSource.class); 066 067 /** List of sources files */ 068 protected List sources; 069 070 public static String request; 071 072 /** List of sources files */ 073 protected List adapters; 074 075 private List categories; 076 077 /** for unpersistence */ 078 protected String oldSourceFromBundles; 079 080 private static final String DATA_DESCRIPTION = "HRIT Data"; 081 082 private static int counter = 1; 083 084 /** children choices */ 085 private List myDataChoices = new ArrayList(); 086 087 /** 088 * Default constructor 089 */ 090 public HRITDataSource() {} 091 092 /** 093 * Construct a new HRIT data source. 094 * @param descriptor descriptor for this {@code DataSource} 095 * @param fileName name of the HRIT segment file to read 096 * @param properties hashtable of properties 097 * 098 * @throws VisADException problem creating data 099 */ 100 public HRITDataSource(DataSourceDescriptor descriptor, 101 String fileName, Hashtable properties) 102 throws VisADException { 103 this(descriptor, Misc.newList(fileName), properties); 104 } 105 106 /** 107 * Construct a new HRIT data source. 108 * @param descriptor descriptor for this {@code DataSource} 109 * @param newSources List of filenames 110 * @param properties hashtable of properties 111 * 112 * @throws VisADException problem creating data 113 */ 114 public HRITDataSource(DataSourceDescriptor descriptor, 115 List newSources, Hashtable properties) 116 throws VisADException { 117 118 this(descriptor, newSources, DATA_DESCRIPTION, properties); 119 boolean looksOk = false; 120 String dataCategoryStr = "HRIT Data"; 121 if ((newSources != null) && (newSources.size() >= 1)) { 122 String fileNameFullPath = (String) newSources.get(0); 123 if ((fileNameFullPath != null) && (fileNameFullPath.length() >= 58)) { 124 if ((fileNameFullPath.contains("MSG2")) && (fileNameFullPath.endsWith("-__"))) { 125 String channelStr = fileNameFullPath.substring(fileNameFullPath.lastIndexOf("MSG2") + 13, fileNameFullPath.lastIndexOf("MSG2") + 19); 126 String timeStr = fileNameFullPath.substring(fileNameFullPath.lastIndexOf("MSG2") + 33, fileNameFullPath.lastIndexOf("MSG2") + 45); 127 dataCategoryStr = "MSG2 " + channelStr + " " + timeStr; 128 looksOk = true; 129 } 130 } 131 } 132 if (looksOk) { 133 DataCategory.createCategory(dataCategoryStr); 134 categories = DataCategory.parseCategories(dataCategoryStr + ";IMAGE"); 135 } else { 136 throw new VisADException("Not a decompressed MSG HRIT file"); 137 } 138 } 139 140 /** 141 * Create a HRITDataSource 142 * 143 * @param descriptor The datasource descriptor 144 * @param newSources List of files or urls 145 * @param description The long name 146 * @param properties properties 147 * 148 * @throws VisADException couldn't create the data 149 */ 150 public HRITDataSource(DataSourceDescriptor descriptor, List newSources, 151 String description, Hashtable properties) 152 throws VisADException { 153 154 super(descriptor, "HRIT" + counter, "HRIT" + counter, properties); 155 counter++; 156 sources = newSources; 157 } 158 159 160 /** 161 * Can this data source save its data to local disk 162 * 163 * @return can save to local disk 164 */ 165 public boolean canSaveDataToLocalDisk() { 166 return !isFileBased() && (getProperty(PROP_SERVICE_HTTP) != null); 167 } 168 169 170 /** 171 * Are we getting data from a file or from server 172 * 173 * @return is the data from files 174 */ 175 protected boolean isFileBased() { 176 if (sources.size() == 0) { 177 return false; 178 } 179 return (new File(sources.get(0).toString())).exists(); 180 } 181 182 /** 183 * This is called when the CacheManager detects the need ot clear memory. 184 * It is intended to be overwritten by derived classes that are holding cached 185 * data that is not in the normal putCache facilities provided by this class 186 * since that data is actually managed by the CacheManager 187 */ 188 public void clearCachedData() { 189 super.clearCachedData(); 190 } 191 192 /** 193 * Make and insert the {@link DataChoice DataChoices} for this {@code DataSource}. 194 */ 195 public void doMakeDataChoices() { 196 DataChoice choice = null; 197 198 for (int i = 0; i < sources.size(); i++) { 199 String fileNameFullPath = (String) sources.get(i); 200 if (fileNameFullPath.contains("MSG2")) { 201 String channelStr = fileNameFullPath.substring(fileNameFullPath.lastIndexOf("MSG2") + 13, fileNameFullPath.lastIndexOf("MSG2") + 19); 202 String timeStr = fileNameFullPath.substring(fileNameFullPath.lastIndexOf("MSG2") + 33, fileNameFullPath.lastIndexOf("MSG2") + 45); 203 String segStr = fileNameFullPath.substring(fileNameFullPath.lastIndexOf("MSG2") + 27, fileNameFullPath.lastIndexOf("MSG2") + 29); 204 try { 205 choice = doMakeDataChoice(0, "MSG2 " + channelStr + " " + timeStr + " SEGMENT " + segStr); 206 } catch (Exception e) { 207 logger.error("Problem creating dataChoice", e); 208 } 209 210 if (choice != null) { 211 addDataChoice(choice); 212 } 213 } 214 } 215 216 } 217 218 private DataChoice doMakeDataChoice(int idx, String var) throws Exception { 219 String name = var; 220 Hashtable ht = null; 221 DirectDataChoice ddc = new DirectDataChoice(this, idx, name, name, categories, ht); 222 return ddc; 223 } 224 225 /** 226 * Create, if needed, and return the list of adapters. 227 * Will return null if there are no valid adapters. 228 * 229 * @return List of adapters or null 230 */ 231 protected List getAdapters() { 232 if ((adapters == null) || (adapters.size() == 0)) { 233 try { 234 makeAdapters(sources); 235 } catch (Exception exc) { 236 setInError(true); 237 throw new WrapperException(exc); 238 } 239 } 240 if (adapters.size() == 0) { 241 adapters = null; 242 } 243 return adapters; 244 } 245 246 /** 247 * Make the adapters for the given list of files 248 * 249 * @param files Data files 250 * 251 * @throws Exception When bad things happen 252 */ 253 private void makeAdapters(List files) throws Exception { 254 adapters = new ArrayList(); 255 } 256 257 258 /** 259 * Create the list of times associated with this DataSource. 260 * @return list of times. 261 */ 262 protected List doMakeDateTimes() { 263 List times = new ArrayList(); 264 return times; 265 } 266 267 /** 268 * Get the data for the given DataChoice and selection criteria. 269 * @param dataChoice DataChoice for selection 270 * @param category DataCategory for the DataChoice (not used) 271 * @param dataparams Resolution criteria. 272 * @param requestProperties extra request properties 273 * @return the Data object for the request 274 * 275 * @throws RemoteException couldn't create a remote data object 276 * @throws VisADException couldn't create the data 277 */ 278 protected Data getDataInner(DataChoice dataChoice, DataCategory category, 279 DataSelection dataparams, 280 Hashtable requestProperties) 281 throws VisADException, RemoteException { 282 283 // for now, hardcoded array of band center wave numbers, such that' 284 // the array index is the band number 285 String[] bandCWN = { 286 "N/A", "006", "008", "016", "039", "062", "073", 287 "087", "097", "108", "120", "134", "___" 288 }; 289 290 // XXX TJJ need to determine this from data type and wavelength 291 int bandNum = 1; 292 // default to BRIT calibration, will check if user picked something else 293 int calType = Calibrator.CAL_BRIT; 294 295 String newRes = (String) dataparams.getProperty("magnification"); 296 int magFactor = 1; 297 if (newRes != null) { 298 try { 299 magFactor = Integer.parseInt(newRes); 300 } catch (NumberFormatException nfe) { 301 String msg = String.format("Could not parse '%s'", newRes); 302 logger.error(msg, nfe); 303 } 304 } 305 306 // pull out source index 307 String idxStr = dataChoice.getName().substring(dataChoice.getName().length() - 2, dataChoice.getName().length()); 308 309 Data data = null; 310 311 String [] files = new String[1]; 312 // initialize central wave number string 313 String cwnStr = "006"; 314 for (int i = 0; i < sources.size(); i++) { 315 String tmpStr = (String) sources.get(i); 316 cwnStr = tmpStr.substring(tmpStr.lastIndexOf("MSG2") + 16, tmpStr.lastIndexOf("MSG2") + 19); 317 String segStr = tmpStr.substring(tmpStr.lastIndexOf("MSG2") + 27, tmpStr.lastIndexOf("MSG2") + 29); 318 if (segStr.equals(idxStr)) { 319 files[0] = (String) sources.get(i); 320 } 321 } 322 323 // match up central wave number with band number index 324 for (int i = 0; i < bandCWN.length; i++) { 325 if (bandCWN[i].equals(cwnStr)) { 326 bandNum = i; 327 break; 328 } 329 } 330 331 String newCal = (String) dataparams.getProperty("calibration"); 332 // do checks to only allow valid calibrations here 333 if (newCal != null) { 334 if ((bandNum >= 4) && (bandNum <= 11)) { 335 if (newCal.equals("RAD")) { 336 calType = Calibrator.CAL_RAD; 337 } 338 if (newCal.equals("TEMP")) { 339 calType = Calibrator.CAL_TEMP; 340 } 341 if (newCal.equals("BRIT")) { 342 calType = Calibrator.CAL_BRIT; 343 } 344 } else { 345 if (newCal.equals("RAD")) { 346 calType = Calibrator.CAL_RAD; 347 } 348 if (newCal.equals("ALB")) { 349 calType = Calibrator.CAL_ALB; 350 } 351 if (newCal.equals("BRIT")) { 352 calType = Calibrator.CAL_BRIT; 353 } 354 } 355 } 356 357 HRITAdapter ha; 358 try { 359 ha = new HRITAdapter(files, magFactor, calType, bandNum); 360 data = ha.getData(); 361 } catch (IOException e) { 362 logger.error("Problem getting data", e); 363 } 364 365 return data; 366 } 367 368 protected void initDataSelectionComponents( 369 List<DataSelectionComponent> components, 370 final DataChoice dataChoice) { 371 372 try { 373 components.add(new ResolutionSelection(dataChoice)); 374 } catch (Exception e) { 375 logger.error("Problem creating new ResolutionSelection", e); 376 } 377 } 378 379 380 class ResolutionSelection extends DataSelectionComponent { 381 382 DataChoice dataChoice; 383 JPanel display; 384 JComboBox jcbMag = null; 385 JComboBox jcbCal = null; 386 387 ResolutionSelection(DataChoice dataChoice) throws Exception { 388 super("Magnification and Calibration"); 389 this.dataChoice = dataChoice; 390 List names = dataChoice.getCurrentNames(); 391 display = new JPanel(new FlowLayout()); 392 String[] resStrings = { "1", "2", "4", "8", "16" }; 393 jcbMag = new JComboBox(resStrings); 394 display.add(jcbMag); 395 String[] irCalStrings = { "BRIT", "RAD", "RAW", "TEMP" }; 396 String[] visCalStrings = { "BRIT", "RAD", "RAW", "ALB" }; 397 // XXX TJJ - we need a standard mechanism to make this determination 398 // this is a temporary cheap hack: grab the last file name added and 399 // do a hardcoded string match. 400 String sampleFileName = names.get(names.size() - 1).toString(); 401 // those below are considered "visible" bands, yes even IR_016! 402 if ((sampleFileName.contains("VIS")) || 403 (sampleFileName.contains("HRV")) || 404 (sampleFileName.contains("IR_016")) 405 ) { 406 jcbCal = new JComboBox(visCalStrings); 407 } else { 408 jcbCal = new JComboBox(irCalStrings); 409 } 410 display.add(jcbCal); 411 } 412 413 protected JComponent doMakeContents() { 414 try { 415 JPanel panel = new JPanel(new BorderLayout()); 416 panel.add("Center", display); 417 return panel; 418 } 419 catch (Exception e) { 420 System.out.println(e); 421 } 422 return null; 423 } 424 425 public void applyToDataSelection(DataSelection dataSelection) { 426 try { 427 dataSelection.putProperty("magnification", jcbMag.getSelectedItem()); 428 dataSelection.putProperty("calibration", jcbCal.getSelectedItem()); 429 } catch (Exception e) { 430 logger.error("Problem applying changes to data selection", e); 431 } 432 } 433 } 434}