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