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