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.chooser.adde; 030 031import static javax.swing.GroupLayout.DEFAULT_SIZE; 032import static javax.swing.GroupLayout.PREFERRED_SIZE; 033import static javax.swing.GroupLayout.Alignment.BASELINE; 034import static javax.swing.GroupLayout.Alignment.LEADING; 035import static javax.swing.LayoutStyle.ComponentPlacement.RELATED; 036 037import java.awt.event.ItemEvent; 038import java.awt.event.ItemListener; 039import java.util.Hashtable; 040import java.util.List; 041 042import javax.swing.BorderFactory; 043import javax.swing.GroupLayout; 044import javax.swing.JCheckBox; 045import javax.swing.JComponent; 046import javax.swing.JLabel; 047import javax.swing.JPanel; 048import javax.swing.JSlider; 049import javax.swing.event.ChangeEvent; 050import javax.swing.event.ChangeListener; 051 052import org.w3c.dom.Element; 053 054import ucar.unidata.data.DataSelection; 055import ucar.unidata.data.imagery.AddeImageDescriptor; 056import ucar.unidata.data.imagery.BandInfo; 057import ucar.unidata.data.imagery.ImageDataset; 058import ucar.unidata.idv.chooser.IdvChooserManager; 059import ucar.unidata.util.GuiUtils; 060import ucar.unidata.util.StringUtil; 061import ucar.unidata.util.TwoFacedObject; 062import ucar.unidata.xml.XmlObjectStore; 063 064import edu.wisc.ssec.mcidas.AreaDirectory; 065import edu.wisc.ssec.mcidasv.Constants; 066import edu.wisc.ssec.mcidasv.util.McVGuiUtils; 067 068/** 069 * Chooser that allows users to select images from a remote ADDE server. 070 * 071 * <p>Displays a list of the descriptors (names) of the image datasets 072 * available for a particular ADDE group on the remote server.</p> 073 */ 074public class ImageChooser extends AddeImageChooser implements Constants { 075 076 private static final long serialVersionUID = 1L; 077 078 /** 079 * Public keys for server, group, dataset, user, project. 080 */ 081 public final static String SIZE_KEY = "size"; 082 public final static String BAND_KEY = "band"; 083 public final static String PLACE_KEY = "place"; 084 public final static String LATLON_KEY = "latlon"; 085 public final static String LINELE_KEY = "linele"; 086 public final static String MAG_KEY = "mag"; 087 public final static String UNIT_KEY = "unit"; 088 public final static String PREVIEW_KEY = "preview"; 089 public final static String NAVIGATION_KEY = "navigation"; 090 091 /** Property for image default value unit */ 092 protected static final String PROP_NAV = "NAV"; 093 094 /** Property for image default value unit */ 095 protected static final String PROP_UNIT = "UNIT"; 096 097 /** Property for image default value band */ 098 protected static final String PROP_BAND = "BAND"; 099 100 /** Xml attr name for the defaults */ 101 private static final String ATTR_NAV = "NAV"; 102 private static final String ATTR_UNIT = "UNIT"; 103 private static final String ATTR_BAND = "BAND"; 104 private static final String ATTR_PLACE = "PLACE"; 105 private static final String ATTR_SIZE = "SIZE"; 106 private static final String ATTR_MAG = "MAG"; 107 private static final String ATTR_LATLON = "LATLON"; 108 private static final String ATTR_LINELE = "LINELE"; 109 110 /** string for ALL */ 111 private static final String ALL = "ALL"; 112 113 private JCheckBox previewBox = null; 114 115 116 /** 117 * Construct an ADDE image selection widget. 118 * 119 * @param mgr Chooser manager. 120 * @param root Chooser XML node. 121 */ 122 public ImageChooser(IdvChooserManager mgr, Element root) { 123 super(mgr, root); 124 //DAVEP: Hiding parameter set picker for now... revisit after 1.0 125// showParameterButton(); 126 } 127 128 /** 129 * Return the parameter type associated with this chooser. 130 */ 131 @Override protected String getParameterSetType() { 132 return "addeimagery"; 133 } 134 135 /** 136 * Return the data source ID. Used by extending classes. 137 */ 138 @Override protected String getDataSourceId() { 139 return "ADDE.IMAGE"; 140 } 141 142 /** 143 * Restore the selected parameter set using element attributes. 144 * 145 * @param restoreElement {@code Element} with the desired attributes. 146 * {@code null} values are permitted. 147 * 148 * @return {@code true} if the parameter set was restored, {@code false} 149 * otherwise. 150 */ 151 @Override protected boolean restoreParameterSet(Element restoreElement) { 152 boolean okay = super.restoreParameterSet(restoreElement); 153 if (!okay) { 154 return okay; 155 } 156 157 // Imagery specific restore 158 159 // Restore nav 160 if (restoreElement.hasAttribute(ATTR_NAV)) { 161 String nav = restoreElement.getAttribute(ATTR_NAV); 162 TwoFacedObject tfo = new TwoFacedObject("Default", "X"); 163 navComboBox.setSelectedItem(tfo); 164 if (nav.toUpperCase().equals("LALO")) { 165 tfo = new TwoFacedObject("Lat/Lon", "LALO"); 166 } 167 navComboBox.setSelectedItem(tfo); 168 } 169 return true; 170 } 171 172 /** 173 * Get the list of BandInfos for the current selected images. 174 * 175 * @return List of BandInfos. 176 */ 177 public List<BandInfo> getSelectedBandInfos() { 178 return super.getBandInfos(); 179 } 180 181 /** 182 * Get the value for the given property. This can either be the value 183 * supplied by the end user through the advanced GUI or is the default. 184 * 185 * @param prop The property. 186 * @param ad The AreaDirectory. 187 * 188 * @return Value of the property to use in the request string. 189 */ 190 @Override protected String getPropValue(String prop, AreaDirectory ad) { 191 String propValue = super.getPropValue(prop, ad); 192 if (PROP_NAV.equals(prop)) { 193 propValue = TwoFacedObject.getIdString(navComboBox.getSelectedItem()); 194 } 195 return propValue; 196 } 197 198 /** 199 * Get the default value for a key. 200 * 201 * @param property Property (key type). 202 * @param dflt Default value. 203 * 204 * @return Value for key or {@code dflt} if not found. 205 */ 206 @Override protected String getDefault(String property, String dflt) { 207 String paramDefault = super.getDefault(property, dflt); 208 if (PROP_NAV.equals(property)) { 209 if (restoreElement != null) { 210 paramDefault = restoreElement.getAttribute(ATTR_NAV); 211 } 212 } else if (PROP_UNIT.equals(property)) { 213 paramDefault = ""; 214 } else if (PROP_BAND.equals(property)) { 215 paramDefault = ALL; 216 } else if (PROP_PLACE.equals(property)) { 217 paramDefault = ""; 218 } 219 return paramDefault; 220 } 221 222 /** 223 * Get the DataSource properties. 224 * 225 * @param ht Hashtable of properties. 226 */ 227 @Override protected void getDataSourceProperties(Hashtable ht) { 228 super.getDataSourceProperties(ht); 229 if (restoreElement != null) { 230 if (restoreElement.hasAttribute(ATTR_BAND)) { 231 ht.put(BAND_KEY, restoreElement.getAttribute(ATTR_BAND)); 232 } 233 if (restoreElement.hasAttribute(ATTR_LATLON)) { 234 ht.put(LATLON_KEY, restoreElement.getAttribute(ATTR_LATLON)); 235 } 236 if (restoreElement.hasAttribute(ATTR_LINELE)) { 237 ht.put(LINELE_KEY, restoreElement.getAttribute(ATTR_LINELE)); 238 } 239 if (restoreElement.hasAttribute(ATTR_MAG)) { 240 ht.put(MAG_KEY, restoreElement.getAttribute(ATTR_MAG)); 241 } 242 if (restoreElement.hasAttribute(ATTR_PLACE)) { 243 ht.put(PLACE_KEY, restoreElement.getAttribute(ATTR_PLACE)); 244 } 245 if (restoreElement.hasAttribute(ATTR_SIZE)) { 246 ht.put(SIZE_KEY, restoreElement.getAttribute(ATTR_SIZE)); 247 } 248 if (restoreElement.hasAttribute(ATTR_UNIT)) { 249 ht.put(UNIT_KEY, restoreElement.getAttribute(ATTR_UNIT)); 250 } 251 } else { 252 ht.put(NAVIGATION_KEY, getPropValue(PROP_NAV, null)); 253 } 254 ht.put(PREVIEW_KEY, previewBox.isSelected()); 255 } 256 257 /** 258 * Should we use the user supplied property. 259 * 260 * @param propId The property. 261 * 262 * @return Should use the value from the advanced widget. 263 */ 264 protected boolean usePropFromUser(String propId) { 265 boolean fromSuper = super.usePropFromUser(propId); 266 if (PROP_UNIT.equals(propId) || PROP_BAND.equals(propId)) { 267 fromSuper = false; 268 } 269 return fromSuper; 270 } 271 272 /** 273 * Make the UI for this selector. 274 * 275 * @return The GUI. 276 */ 277 @Override public JComponent doMakeContents() { 278 JPanel myPanel = new JPanel(); 279 280 JLabel timesLabel = McVGuiUtils.makeLabelRight("Times:"); 281 addDescComp(timesLabel); 282 283 JPanel timesPanel = makeTimesPanel(); 284 timesPanel.setBorder(BorderFactory.createEtchedBorder()); 285 addDescComp(timesPanel); 286 287 JLabel navigationLabel = McVGuiUtils.makeLabelRight("Navigation:"); 288 addDescComp(navigationLabel); 289 290 // Use processPropertyComponents to build combo boxes that we rely on 291 processPropertyComponents(); 292 addDescComp(navComboBox); 293 McVGuiUtils.setComponentWidth(navComboBox, McVGuiUtils.Width.DOUBLE); 294 295 // Preview checkbox 296 JLabel previewLabel = McVGuiUtils.makeLabelRight("Preview:"); 297 addDescComp(previewLabel); 298 XmlObjectStore store = getIdv().getStore(); 299 previewBox = new JCheckBox("Create preview image", store.get(Constants.PREF_IMAGE_PREVIEW, true)); 300 previewBox.setToolTipText("Creating preview images takes extra time and network bandwidth"); 301 previewBox.addItemListener(new ItemListener() { 302 public void itemStateChanged(ItemEvent e) { 303 XmlObjectStore store = getIdv().getStore(); 304 store.put(Constants.PREF_IMAGE_PREVIEW, previewBox.isSelected()); 305 store.save(); 306 } 307 }); 308 addDescComp(previewBox); 309 310 GroupLayout layout = new GroupLayout(myPanel); 311 myPanel.setLayout(layout); 312 layout.setHorizontalGroup( 313 layout.createParallelGroup(LEADING) 314 .addGroup(layout.createSequentialGroup() 315 .addGroup(layout.createParallelGroup(LEADING) 316 .addGroup(layout.createSequentialGroup() 317 .addComponent(descriptorLabel) 318 .addGap(GAP_RELATED) 319 .addComponent(descriptorComboBox)) 320 .addGroup(layout.createSequentialGroup() 321 .addComponent(timesLabel) 322 .addGap(GAP_RELATED) 323 .addComponent(timesPanel, PREFERRED_SIZE, DEFAULT_SIZE, Short.MAX_VALUE)) 324 .addGroup(layout.createSequentialGroup() 325 .addComponent(navigationLabel) 326 .addGap(GAP_RELATED) 327 .addComponent(navComboBox)) 328 .addGroup(layout.createSequentialGroup() 329 .addComponent(previewLabel) 330 .addGap(GAP_RELATED) 331 .addComponent(previewBox)))) 332 ); 333 layout.setVerticalGroup( 334 layout.createParallelGroup(LEADING) 335 .addGroup(layout.createSequentialGroup() 336 .addGroup(layout.createParallelGroup(BASELINE) 337 .addComponent(descriptorLabel) 338 .addComponent(descriptorComboBox)) 339 .addPreferredGap(RELATED) 340 .addGroup(layout.createParallelGroup(LEADING) 341 .addComponent(timesLabel) 342 .addComponent(timesPanel, PREFERRED_SIZE, DEFAULT_SIZE, Short.MAX_VALUE)) 343 .addPreferredGap(RELATED) 344 .addGroup(layout.createParallelGroup(LEADING) 345 .addComponent(navigationLabel) 346 .addComponent(navComboBox)) 347 .addGroup(layout.createParallelGroup(LEADING) 348 .addComponent(previewLabel) 349 .addComponent(previewBox))) 350 ); 351 setInnerPanel(myPanel); 352 return super.doMakeContents(true); 353 } 354 355 /** 356 * User said go, we go. Simply get the list of images from the Image Chooser 357 * and create the {@code ADDE.IMAGE} data source. 358 */ 359 @Override public void doLoadInThread() { 360 if (!checkForValidValues()) { 361 return; 362 } 363 if (!getGoodToGo()) { 364 updateStatus(); 365 return; 366 } 367 368 List imageList = getImageList(); 369 if ((imageList == null) || imageList.isEmpty()) { 370 return; 371 } 372 373 // Check for size threshold 374 final int[] dim = { 0, 0 }; 375 AddeImageDescriptor aid = (AddeImageDescriptor) imageList.get(0); 376 dim[0] = aid.getImageInfo().getElements(); 377 dim[1] = aid.getImageInfo().getLines(); 378 // System.err.println("dim:" + dim[0] + " x " + dim[1] + " # images:" 379 // + imageList.size()); 380 int numPixels = dim[0] * dim[1] * imageList.size(); 381 double megs = (4 * numPixels) / (double) 1000000; 382 383 //DAVEP: take this out--it should be warning in the data source, not the chooser 384 boolean doSizeCheck = false; 385 if ((megs > AddeImageChooser.SIZE_THRESHOLD) && doSizeCheck) { 386 final JCheckBox maintainSize = new JCheckBox( 387 "Maintain spatial extent", false); 388 final JLabel sizeLbl = new JLabel(StringUtil.padRight(" " 389 + ((double) ((int) megs * 100)) / 100.0 + " MB", 14)); 390 GuiUtils.setFixedWidthFont(sizeLbl); 391 final List[] listHolder = { imageList }; 392 final JSlider slider = new JSlider(2, (int) megs, (int) megs); 393 slider.setMajorTickSpacing((int) (megs - 2) / 10); 394 slider.setMinorTickSpacing((int) (megs - 2) / 10); 395 // slider.setPaintTicks(true); 396 slider.setSnapToTicks(true); 397 final long timeNow = System.currentTimeMillis(); 398 ChangeListener sizeListener = new javax.swing.event.ChangeListener() { 399 public void stateChanged(ChangeEvent evt) { 400 // A hack so we don't respond to the first event that we get 401 // from the slider when 402 // the dialog is first shown 403 if ((System.currentTimeMillis() - timeNow) < 500) { 404 return; 405 } 406 JSlider slider = (JSlider) evt.getSource(); 407 int pixelsPerImage = (1000000 * slider.getValue()) / listHolder[0].size() / 4; 408 double aspect = dim[1] / (double) dim[0]; 409 int nx = (int) Math.sqrt(pixelsPerImage / aspect); 410 int ny = (int) (aspect * nx); 411 if (maintainSize.isSelected()) { 412 // doesn't work 413 lineMagSlider.setValue(getLineMagValue() - 1); 414 lineMagSliderChanged(true); 415 } else { 416 numElementsFld.setText("" + nx); 417 numLinesFld.setText("" + ny); 418 } 419 listHolder[0] = getImageList(); 420 AddeImageDescriptor aid = (AddeImageDescriptor) listHolder[0] 421 .get(0); 422 dim[0] = aid.getImageInfo().getElements(); 423 dim[1] = aid.getImageInfo().getLines(); 424 int numPixels = dim[0] * dim[1] * listHolder[0].size(); 425 double nmegs = (4 * numPixels) / (double) 1000000; 426 sizeLbl.setText(StringUtil.padRight(" " 427 + ((double) ((int) nmegs * 100)) / 100.0 + " MB", 428 14)); 429 } 430 }; 431 slider.addChangeListener(sizeListener); 432 433 JComponent msgContents = GuiUtils 434 .vbox( 435 new JLabel( 436 "<html>You are about to load " 437 + megs 438 + " MB of imagery.<br>Are you sure you want to do this?<p><hr><p></html>"), 439 GuiUtils.inset(GuiUtils.leftCenterRight(new JLabel( 440 "Change Size: "), 441 GuiUtils.inset(slider, 5), sizeLbl), 5)); 442 443 if (!GuiUtils.askOkCancel("Image Size", msgContents)) { 444 return; 445 } 446 imageList = listHolder[0]; 447 } 448 449 ImageDataset ids = new ImageDataset(getDatasetName(), imageList); 450 // make properties Hashtable to hand the station name 451 // to the AddeImageDataSource 452 Hashtable ht = new Hashtable(); 453 ht.put(DataSelection.PROP_CHOOSERTIMEMATCHING, getDoTimeDrivers()); 454 getDataSourceProperties(ht); 455 Object bandName = getSelectedBandName(); 456 if ((bandName != null) && !bandName.equals(ALLBANDS.toString())) { 457 ht.put(DATA_NAME_KEY, bandName); 458 } 459 ht.put("allBands", bandDirs); 460 makeDataSource(ids, getDataSourceId(), ht); 461 saveServerState(); 462 // uncheck the check box every time click the add source button 463 drivercbx.setSelected(false); 464 enableTimeWidgets(); 465 setDoTimeDrivers(false); 466 } 467 468}