001/* 002 * This file is part of McIDAS-V 003 * 004 * Copyright 2007-2015 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.data; 030 031import java.awt.BorderLayout; 032import java.awt.Color; 033import java.awt.geom.Rectangle2D; 034import java.net.URL; 035import java.rmi.RemoteException; 036import java.util.Enumeration; 037import java.util.HashMap; 038import java.util.Hashtable; 039 040import javax.swing.JComponent; 041import javax.swing.JOptionPane; 042import javax.swing.JPanel; 043 044import org.slf4j.Logger; 045import org.slf4j.LoggerFactory; 046 047import ucar.unidata.data.DataCategory; 048import ucar.unidata.data.DataChoice; 049import ucar.unidata.data.DataSelection; 050import ucar.unidata.data.DataSelectionComponent; 051import ucar.unidata.data.DataSourceImpl; 052import ucar.unidata.data.DirectDataChoice; 053import ucar.unidata.data.grid.GridUtil; 054import ucar.unidata.idv.DisplayConventions; 055import ucar.unidata.util.ColorTable; 056import ucar.unidata.util.Range; 057import ucar.unidata.view.geoloc.MapProjectionDisplay; 058import ucar.unidata.view.geoloc.MapProjectionDisplayJ3D; 059import ucar.visad.display.DisplayMaster; 060import ucar.visad.display.MapLines; 061 062import visad.BaseColorControl; 063import visad.CellImpl; 064import visad.FlatField; 065import visad.FunctionType; 066import visad.Gridded2DSet; 067import visad.RealType; 068import visad.SampledSet; 069import visad.ScalarMap; 070import visad.VisADException; 071import visad.data.mcidas.BaseMapAdapter; 072import visad.georef.MapProjection; 073 074import edu.wisc.ssec.mcidasv.control.LambertAEA; 075import edu.wisc.ssec.mcidasv.control.RGBCompositeControl; 076import edu.wisc.ssec.mcidasv.data.hydra.HydraContext; 077import edu.wisc.ssec.mcidasv.data.hydra.HydraRGBDisplayable; 078import edu.wisc.ssec.mcidasv.data.hydra.MultiDimensionSubset; 079import edu.wisc.ssec.mcidasv.data.hydra.MultiSpectralData; 080import edu.wisc.ssec.mcidasv.data.hydra.SubsetRubberBandBox; 081 082public class PreviewSelection extends DataSelectionComponent { 083 084 private static final Logger logger = LoggerFactory.getLogger(PreviewSelection.class); 085 086 DataChoice dataChoice; 087 FlatField image; 088 boolean isLL; 089 boolean formulaActive = false; 090 MapProjection sampleProjection; 091 092 double[] x_coords = new double[2]; 093 double[] y_coords = new double[2]; 094 boolean hasSubset = false; 095 boolean selectionOutOfBounds = false; 096 MapProjectionDisplayJ3D mapProjDsp; 097 DisplayMaster dspMaster; 098 099 DataSourceImpl dataSource; 100 101 DataCategory dataCategory; 102 private SubsetRubberBandBox rbb = null; 103 104 static SampledSet lines_outlsupu = null; 105 static SampledSet lines_outlsupw = null; 106 static SampledSet lines_outlhpol = null; 107 108 HydraContext hydraContext = null; 109 110 public PreviewSelection() { 111 super("Region"); 112 } 113 114 public PreviewSelection(final DataChoice dataChoice, FlatField image, 115 MapProjection sample) throws VisADException, RemoteException { 116 this(dataChoice, image, sample, null, null); 117 } 118 119 public PreviewSelection(final DataChoice dataChoice, FlatField image, 120 MapProjection sample, Range displayRange, byte[][] colorTable) throws VisADException, RemoteException { 121 super("Region"); 122 123 this.dataChoice = dataChoice; 124 this.dataCategory = (DataCategory) dataChoice.getCategories().get(0); 125 this.dataSource = (DataSourceImpl) ((DirectDataChoice)dataChoice).getDataSource(); 126 this.image = image; 127 this.sampleProjection = sample; 128 sample = getDataProjection(); 129 130 // TJJ Jul 2014 131 // by sharing a property via the active View Manager, we can tell if 132 // this preview is part of an in-progress VIIRS Formula. If so, it 133 // appears we need to use a shared HydraContext so our geographic 134 // coverage subset applies across channels. The flag is set in 135 // the originating Control init, and reset after the Displayable 136 // was successfully returned. 137 138 Hashtable ht = dataSource.getIdv().getViewManager().getProperties(); 139 if (ht.containsKey(RGBCompositeControl.FORMULA_IN_PROGRESS_FLAG)) { 140 formulaActive = (boolean) ht.get(RGBCompositeControl.FORMULA_IN_PROGRESS_FLAG); 141 } 142 143 DisplayConventions dspConv = dataSource.getDataContext().getIdv().getDisplayConventions(); 144 145 if (this.sampleProjection == null) { 146 this.sampleProjection = sample; 147 } 148 149 isLL = sampleProjection.isLatLonOrder(); 150 151 mapProjDsp = new MapProjectionDisplayJ3D(MapProjectionDisplay.MODE_2Din3D); 152 mapProjDsp.enableRubberBanding(false); 153 dspMaster = mapProjDsp; 154 mapProjDsp.setMapProjection(sampleProjection); 155 RealType imageRangeType = 156 (((FunctionType)image.getType()).getFlatRange().getRealComponents())[0]; 157 HydraRGBDisplayable imageDsp = new HydraRGBDisplayable("image", imageRangeType, null, true, null); 158 imageDsp.setData(image); 159 160 dspMaster.addDisplayable(imageDsp); 161 162 MapLines mapLines = new MapLines("maplines"); 163 URL mapSource = 164 mapProjDsp.getClass().getResource("/auxdata/maps/OUTLSUPU"); 165 try { 166 if (lines_outlsupu == null) { 167 BaseMapAdapter mapAdapter = new BaseMapAdapter(mapSource); 168 lines_outlsupu = (SampledSet) mapAdapter.getData(); 169 } 170 mapLines.setMapLines(lines_outlsupu); 171 mapLines.setColor(java.awt.Color.cyan); 172 mapProjDsp.addDisplayable(mapLines); 173 } catch (Exception excp) { 174 logger.error("Can't open map file " + mapSource); 175 excp.printStackTrace(); 176 } 177 178 mapLines = new MapLines("maplines"); 179 mapSource = 180 mapProjDsp.getClass().getResource("/auxdata/maps/OUTLSUPW"); 181 try { 182 if (lines_outlsupw == null) { 183 BaseMapAdapter mapAdapter = new BaseMapAdapter(mapSource); 184 lines_outlsupw = (SampledSet) mapAdapter.getData(); 185 } 186 mapLines.setMapLines(lines_outlsupw); 187 mapLines.setColor(java.awt.Color.cyan); 188 mapProjDsp.addDisplayable(mapLines); 189 } catch (Exception excp) { 190 logger.error("Can't open map file " + mapSource); 191 excp.printStackTrace(); 192 } 193 194 mapLines = new MapLines("maplines"); 195 mapSource = 196 mapProjDsp.getClass().getResource("/auxdata/maps/OUTLHPOL"); 197 try { 198 if (lines_outlhpol == null) { 199 BaseMapAdapter mapAdapter = new BaseMapAdapter(mapSource); 200 lines_outlhpol = (SampledSet) mapAdapter.getData(); 201 } 202 mapLines.setMapLines(lines_outlhpol); 203 mapLines.setColor(java.awt.Color.cyan); 204 mapProjDsp.addDisplayable(mapLines); 205 } catch (Exception excp) { 206 logger.error("Can't open map file " + mapSource); 207 excp.printStackTrace(); 208 } 209 210 211 212 Hashtable table = dataChoice.getProperties(); 213 Enumeration keys = table.keys(); 214 while (keys.hasMoreElements()) { 215 Object key = keys.nextElement(); 216 if (key instanceof MultiDimensionSubset) { 217 hasSubset = true; 218 MultiDimensionSubset select = (MultiDimensionSubset) table.get(key); 219 220 if (formulaActive) { 221 hydraContext = HydraContext.getHydraContext(); 222 } else { 223 hydraContext = HydraContext.getHydraContext(dataSource, dataCategory); 224 } 225 if (hydraContext.getMultiDimensionSubset() == null) { 226 hydraContext.setMultiDimensionSubset(select); 227 } 228 } 229 } 230 231 rbb = new SubsetRubberBandBox(isLL, image, ((MapProjectionDisplay)mapProjDsp).getDisplayCoordinateSystem(), 1); 232 rbb.setColor(Color.green); 233 rbb.addAction(new CellImpl() { 234 boolean init = false; 235 236 public void doAction() 237 throws VisADException, RemoteException 238 { 239 240 if (!init) { 241 init = true; 242 return; 243 } 244 Gridded2DSet set = rbb.getBounds(); 245 246 float[] low = set.getLow(); 247 float[] hi = set.getHi(); 248 249 // TJJ Apr 2014 250 // The fact that we can even get here with invalid bounding boxes 251 // (edges == Infinity) is another problem that should be investigated. 252 // For now we should at least let the user know they selected off 253 // the valid data bounds 254 255 if ((low[0] == Float.NEGATIVE_INFINITY) || (low[0] == Float.POSITIVE_INFINITY)) 256 selectionOutOfBounds = true; 257 if ((hi[0] == Float.NEGATIVE_INFINITY) || (hi[0] == Float.POSITIVE_INFINITY)) 258 selectionOutOfBounds = true; 259 if ((low[1] == Float.NEGATIVE_INFINITY) || (low[1] == Float.POSITIVE_INFINITY)) 260 selectionOutOfBounds = true; 261 if ((hi[1] == Float.NEGATIVE_INFINITY) || (hi[1] == Float.POSITIVE_INFINITY)) 262 selectionOutOfBounds = true; 263 264 if (selectionOutOfBounds) { 265 JOptionPane.showMessageDialog(null, 266 "Data selection is not valid, please select within preview bounds", 267 "Data Selection Error", JOptionPane.ERROR_MESSAGE); 268 selectionOutOfBounds = false; 269 return; 270 } 271 272 // TJJ Mar 2014 273 // The checks below are because the subset rubber-band box selector is 274 // able to select regions outside the data bounds, which causes 275 // errors. The simplest solution was to check the selection bounds 276 // and constrain them if they go outside data bounds. 277 278 x_coords[0] = low[0]; 279 if (x_coords[0] < 0) { 280 logger.debug("Constraining X lo bound: " + low[0] + " to: " + 0); 281 x_coords[0] = 0; 282 } 283 x_coords[1] = hi[0]; 284 int lineMax = rbb.getLineMax(); 285 if (x_coords[1] > lineMax) { 286 logger.debug("Constraining X hi bound: " + hi[0] + " to: " + lineMax); 287 x_coords[1] = lineMax; 288 } 289 290 y_coords[0] = low[1]; 291 if (y_coords[0] < 0) { 292 logger.debug("Constraining Y lo bound: " + low[1] + " to: " + 0); 293 y_coords[0] = 0; 294 } 295 y_coords[1] = hi[1]; 296 int elemMax = rbb.getElemMax(); 297 if (y_coords[1] > elemMax) { 298 logger.debug("Constraining Y hi bound: " + hi[1] + " to: " + elemMax); 299 y_coords[1] = elemMax; 300 } 301 302 if (hasSubset) { 303 MultiDimensionSubset select = hydraContext.getMultiDimensionSubset(); 304 HashMap map = select.getSubset(); 305 306 double[] coords0 = (double[]) map.get("Track"); 307 coords0[0] = y_coords[0]; 308 coords0[1] = y_coords[1]; 309 coords0[2] = 1; 310 double[] coords1 = (double[]) map.get("XTrack"); 311 coords1[0] = x_coords[0]; 312 coords1[1] = x_coords[1]; 313 coords1[2] = 1; 314 315 hydraContext.setMultiDimensionSubset(new MultiDimensionSubset(map)); 316 } 317 } 318 }); 319 dspMaster.addDisplayable(rbb); 320 321 ScalarMap colorMap = imageDsp.getColorMap(); 322 Range[] range = GridUtil.fieldMinMax(this.image); 323 Range imageRange = range[0]; 324 double max; 325 double min; 326 double dMax = imageRange.getMax(); 327 double dMin = imageRange.getMin(); 328 String name = this.dataChoice.getName(); 329 330 float[][] clrTbl = BaseColorControl.initTableGreyWedge(new float[4][256], true); 331 332 if (name.endsWith("BRIT")) { 333 dMin = imageRange.getMin(); 334 min = dMax; 335 max = dMin; 336 } 337 else if (imageRangeType.getName().contains("Reflectance")) { 338 min = dMax; 339 max = 0.0; 340 } 341 else if (imageRangeType.getName().equals("BrightnessTemp")) { 342 max = dMax*1.06; 343 min = dMax * 0.74; 344 } 345 else { 346 Range rng = dspConv.getParamRange(name, null); 347 max = dMax; 348 min = dMin; 349 ColorTable ct = dspConv.getParamColorTable(name); 350 clrTbl = ct.getTable(); 351 } 352 colorMap.setRange(min, max); 353 354 /*- must to draw first so colorMap has a Control */ 355 dspMaster.draw(); 356 357 BaseColorControl clrCntrl = (BaseColorControl) colorMap.getControl(); 358 clrCntrl.setTable(clrTbl); 359 } 360 361 public MapProjection getDataProjection() { 362 MapProjection mp = null; 363 364 if (image == null) return mp; 365 366 Rectangle2D rect = MultiSpectralData.getLonLatBoundingBox(image); 367 try { 368 mp = new LambertAEA(rect); 369 } catch (Exception e) { 370 e.printStackTrace(); 371 } 372 return mp; 373 } 374 375 public JComponent doMakeContents() { 376 JPanel panel = new JPanel(new BorderLayout()); 377 panel.add(BorderLayout.CENTER, dspMaster.getDisplayComponent()); 378 return panel; 379 } 380 381 public void applyToDataSelection(DataSelection dataSelection) { 382 383 if (hasSubset) { 384 Hashtable table = dataChoice.getProperties(); 385 table.put(MultiDimensionSubset.key, hydraContext.getMultiDimensionSubset()); 386 387 table = dataSelection.getProperties(); 388 table.put(MultiDimensionSubset.key, hydraContext.getMultiDimensionSubset()); 389 390 dataChoice.setDataSelection(dataSelection); 391 } 392 393 } 394 395 /** 396 * Enable or disable region subsetting 397 * 398 * @param b true or false 399 */ 400 401 public void enableSubsetting(boolean b) { 402 try { 403 rbb.setVisible(b); 404 } catch (RemoteException | VisADException e) { 405 e.printStackTrace(); 406 } 407 } 408 409 }