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; 030 031import java.awt.BorderLayout; 032import java.awt.Color; 033import java.awt.geom.Rectangle2D; 034import java.awt.event.ComponentEvent; 035import java.awt.event.ComponentListener; 036import java.net.URL; 037import java.rmi.RemoteException; 038 039import javax.swing.JComponent; 040import javax.swing.JPanel; 041 042import visad.BaseColorControl; 043import visad.CellImpl; 044import visad.FlatField; 045import visad.FunctionType; 046import visad.Gridded2DSet; 047import visad.RealTupleType; 048import visad.RealType; 049import visad.ScalarMap; 050import visad.VisADException; 051import visad.data.mcidas.BaseMapAdapter; 052import visad.georef.MapProjection; 053import ucar.visad.display.Displayable; 054import ucar.visad.display.DisplayMaster; 055import ucar.visad.display.LineDrawing; 056import ucar.visad.display.MapLines; 057import ucar.unidata.data.DataChoice; 058import ucar.unidata.data.DataSelection; 059import ucar.unidata.data.DataSourceImpl; 060import ucar.unidata.data.DataSelectionComponent; 061import ucar.unidata.data.GeoSelection; 062import ucar.unidata.data.grid.GridUtil; 063import ucar.unidata.idv.IdvObjectStore; 064import ucar.unidata.idv.MapViewManager; 065import ucar.unidata.idv.ViewManager; 066import ucar.unidata.util.Range; 067import ucar.unidata.view.geoloc.MapProjectionDisplay; 068import ucar.unidata.view.geoloc.MapProjectionDisplayJ3D; 069 070import org.slf4j.Logger; 071import org.slf4j.LoggerFactory; 072 073import edu.wisc.ssec.mcidasv.data.hydra.HydraRGBDisplayable; 074import edu.wisc.ssec.mcidasv.data.hydra.MultiSpectralData; 075import edu.wisc.ssec.mcidasv.control.LambertAEA; 076 077public class GeoPreviewSelection extends DataSelectionComponent { 078 079 private static final Logger logger = LoggerFactory.getLogger(GeoPreviewSelection.class); 080 DataChoice dataChoice; 081 FlatField image; 082 boolean isLL; 083 MapProjection sampleProjection; 084 085 double[] x_coords = new double[2]; 086 double[] y_coords = new double[2]; 087 MapProjectionDisplayJ3D mapProjDsp; 088 DisplayMaster dspMaster; 089 MapViewManager mvm; 090 IdvObjectStore store; 091 092 final private GeoSubsetRubberBandBox rbb; 093 private int lineMag; 094 private int elementMag; 095 096 private GeoLatLonSelection laloSel; 097 098 private LineDrawing box; 099 100 public GeoPreviewSelection(DataSourceImpl dataSource, 101 DataChoice dataChoice, FlatField image, 102 GeoLatLonSelection laLoSel, 103 MapProjection sample, int lMag, int eMag, boolean showPreview) 104 throws VisADException, RemoteException { 105 super("Region"); 106 107 this.dataChoice = dataChoice; 108 this.image = image; 109 this.laloSel = laLoSel; 110 this.sampleProjection = sample; 111 112 if (lMag == 0) lMag = 1; 113 if (eMag == 0) eMag = 1; 114 this.lineMag = lMag; 115 this.elementMag = eMag; 116 sample = getDataProjection(); 117 118 if (this.sampleProjection == null) { 119 this.sampleProjection = sample; 120 } 121 122 isLL = sampleProjection.isLatLonOrder(); 123 mapProjDsp = new MapProjectionDisplayJ3D(MapProjectionDisplay.MODE_2Din3D); 124 mapProjDsp.enableRubberBanding(false); 125 dspMaster = mapProjDsp; 126 mapProjDsp.setMapProjection(sampleProjection); 127 RealType imageRangeType = (((FunctionType)image.getType()).getFlatRange().getRealComponents())[0]; 128 HydraRGBDisplayable imageDsp = new HydraRGBDisplayable("image", imageRangeType, null, true, null); 129 130 String name = this.dataChoice.getName(); 131 132 if (showPreview) { 133 if (name.startsWith("186_")) { 134 filterMissingValueABI(); 135 } 136 imageDsp.setData(image); 137 } 138 139 MapLines mapLines = new MapLines("maplines"); 140 URL mapSource = mapProjDsp.getClass().getResource("/auxdata/maps/OUTLSUPU"); 141 try { 142 BaseMapAdapter mapAdapter = new BaseMapAdapter(mapSource); 143 mapLines.setMapLines(mapAdapter.getData()); 144 mapLines.setColor(java.awt.Color.cyan); 145 mapProjDsp.addDisplayable(mapLines); 146 } catch (Exception excp) { 147 logger.error("can't open map file="+mapSource, excp); 148 } 149 150 mapLines = new MapLines("maplines"); 151 mapSource = mapProjDsp.getClass().getResource("/auxdata/maps/OUTLSUPW"); 152 try { 153 BaseMapAdapter mapAdapter = new BaseMapAdapter(mapSource); 154 mapLines.setMapLines(mapAdapter.getData()); 155 mapLines.setColor(java.awt.Color.cyan); 156 mapProjDsp.addDisplayable(mapLines); 157 } catch (Exception excp) { 158 logger.error("can't open map file="+mapSource, excp); 159 } 160 161 mapLines = new MapLines("maplines"); 162 mapSource = mapProjDsp.getClass().getResource("/auxdata/maps/OUTLHPOL"); 163 try { 164 BaseMapAdapter mapAdapter = new BaseMapAdapter(mapSource); 165 mapLines.setMapLines(mapAdapter.getData()); 166 mapLines.setColor(java.awt.Color.cyan); 167 mapProjDsp.addDisplayable(mapLines); 168 } catch (Exception excp) { 169 logger.error("can't open map file="+mapSource, excp); 170 } 171 172 if (showPreview) { 173 dspMaster.addDisplayable(imageDsp); 174 } 175 rbb = new GeoSubsetRubberBandBox(isLL, image, ((MapProjectionDisplay)mapProjDsp).getDisplayCoordinateSystem(), 1); 176 mvm = new MapViewManager(dataSource.getDataContext().getIdv()); 177 store = dataSource.getDataContext().getIdv().getStore(); 178 rbb.setColor((Color)store.get(ViewManager.PREF_FGCOLOR, Color.GREEN)); 179 rbb.addAction(new CellImpl() { 180 public void doAction() throws VisADException, RemoteException { 181 eraseBox(); 182 forceCoords(); 183 } 184 }); 185 addRBB(); 186 makeBox(); 187 188 dspMaster.draw(); 189 ScalarMap colorMap = imageDsp.getColorMap(); 190 if (showPreview) { 191 Range[] range = GridUtil.fieldMinMax(this.image); 192 Range imageRange = range[0]; 193 int max; 194 int min; 195 double dMax = imageRange.getMax(); 196 double dMin = imageRange.getMin(); 197 DataSelection ds = this.dataChoice.getDataSelection(); 198 if (ds != null) { 199 GeoSelection gs = ds.getGeoSelection(); 200 } 201 if (name.endsWith("TEMP")) { 202 min = (int)(dMax); 203 max = (int)(dMin); 204 } else { 205 max = (int)(dMin); 206 min = (int)(dMax); 207 } 208 colorMap.setRange(min, max); 209 BaseColorControl clrCntrl = (BaseColorControl) colorMap.getControl(); 210 clrCntrl.setTable(BaseColorControl.initTableGreyWedge(new float[4][256], true)); 211 } 212 } 213 214 private void filterMissingValueABI() throws VisADException, RemoteException { 215 float missingValue = getMissingValueABI(); 216 float[][] values = image.getFloats(); 217 for (int i = 0; i < values[0].length; i++) { 218 if (values[0][i] == missingValue) { 219 values[0][i] = Float.NaN; 220 } 221 } 222 image.setSamples(values); 223 } 224 225 private float getMissingValueABI() { 226 String name = this.dataChoice.getName(); 227 if (!name.startsWith("186_")) { 228 throw new AssertionError("This method is only applicable to ABI"); 229 } 230 if (name.contains("_Band1_")) { 231 return 1023.0f; 232 } else if (name.contains("_Band2_")) { 233 return 4095.0f; 234 } else if (name.contains("_Band3_")) { 235 return 1023.0f; 236 } else if (name.contains("_Band4_")) { 237 return 2047.0f; 238 } else if (name.contains("_Band5_")) { 239 return 1023.0f; 240 } else if (name.contains("_Band6_")) { 241 return 1023.0f; 242 } else if (name.contains("_Band7_")) { 243 return 16383.0f; 244 } else if (name.contains("_Band8_")) { 245 return 4095.0f; 246 } else if (name.contains("_Band9_")) { 247 return 2047.0f; 248 } else if (name.contains("_Band10_")) { 249 return 4095.0f; 250 } else if (name.contains("_Band11_")) { 251 return 4095.0f; 252 } else if (name.contains("_Band12_")) { 253 return 2047.0f; 254 } else if (name.contains("_Band13_")) { 255 return 4095.0f; 256 } else if (name.contains("_Band14_")) { 257 return 4095.0f; 258 } else if (name.contains("_Band15_")) { 259 return 4095.0f; 260 } else if (name.contains("_Band16_")) { 261 return 1023.0f; 262 } else { 263 throw new AssertionError("Could not infer band from '"+name+'\''); 264 } 265 } 266 267 public MapProjection getDataProjection() { 268 MapProjection mp = null; 269 Rectangle2D rect = MultiSpectralData.getLonLatBoundingBox(image); 270 try { 271 mp = new LambertAEA(rect); 272 } catch (Exception e) { 273 logger.error("error while attempting to create new LambertAEA", e); 274 } 275 return mp; 276 } 277 278 public void initBox() { 279 this.drawBox(); 280 } 281 282 protected JComponent doMakeContents() { 283 try { 284 JPanel panel = new JPanel(new BorderLayout()); 285 panel.add("Center", dspMaster.getDisplayComponent()); 286 panel.addComponentListener (new ComponentListener() { 287 public void componentHidden(ComponentEvent ce) { 288 dspMaster.getDisplayComponent().setVisible(false); 289 } 290 public void componentShown(ComponentEvent ce) { 291 dspMaster.getDisplayComponent().setVisible(true); 292 drawBox(); 293 rbb.resetExtremes(); 294 } 295 public void componentMoved(ComponentEvent ce) { 296 } 297 public void componentResized(ComponentEvent ce) { 298 } 299 }); 300 return panel; 301 } 302 catch (Exception e) { 303 logger.error("error building preview panel", e); 304 } 305 return null; 306 } 307 308 public void setDataChoice(DataChoice choice) { 309 logger.trace("oldChoice={} newChoice={}", this.dataChoice, choice); 310 this.dataChoice = choice; 311 } 312 public DataChoice getDataChoice() { 313 return this.dataChoice; 314 } 315 316 private void forceCoords() { 317 float[] extrms = rbb.getRanges(); 318 x_coords[0] = (double)extrms[0]; 319 y_coords[0] = (double)extrms[1]; 320 x_coords[1] = (double)extrms[2]; 321 y_coords[1] = (double)extrms[3]; 322 323 int height = (int)(y_coords[1] - y_coords[0]); 324 int width = (int)(x_coords[1] - x_coords[0]); 325 if ((height < 1) || (width < 1)) return; 326 327 if (laloSel != null) { 328 int lineMid = (int)((y_coords[0] + y_coords[1])/2.0 + 0.5); 329 int eleMid = (int)((x_coords[0] + x_coords[1])/2.0 + 0.5); 330 double uLLine = y_coords[1]; 331 double uLEle = x_coords[0]; 332 if (height < 0) { 333 height *= -1; 334 uLLine = y_coords[0]; 335 } 336 if (width < 0) { 337 width *= -1; 338 uLEle = x_coords[1]; 339 } 340 341 int line = lineMid; 342 int ele = eleMid; 343 if (laloSel.getPlace().equals(GeoLatLonSelection.PLACE_ULEFT)) { 344 line = (int)Math.floor(uLLine + 0.5); 345 ele = (int)Math.floor(uLEle + 0.5); 346 } 347 348 int linRes = laloSel.getPreviewLineRes(); 349 int eleRes = laloSel.getPreviewEleRes(); 350 351 height *= linRes; 352 width *= eleRes; 353 laloSel.setBaseNumLines(height); 354 laloSel.setBaseNumElements(width); 355 356 this.lineMag = laloSel.getLineMag(); 357 this.elementMag = laloSel.getElementMag(); 358 if (lineMag > 0) { 359 height *= lineMag; 360 } else if (lineMag < 0) { 361 height /= -lineMag; 362 } 363 if (elementMag > 0) { 364 width *= elementMag; 365 } else if (elementMag < 0) { 366 width /= -elementMag; 367 } 368 369 Rectangle2D mapArea = sampleProjection.getDefaultMapArea(); 370 double previewXDim = mapArea.getWidth(); 371 double previewYDim = mapArea.getHeight(); 372 double dLin = (double)line; 373 double dEle = (double)ele; 374 if ((line < 0) || (dLin > previewYDim) || 375 (ele < 0) || (dEle > previewXDim)) { 376 line = -1; 377 ele = -1; 378 } 379 380// boolean lock = laloSel.getLockOn(); 381// laloSel.setLockOn(true); 382// int lineMag = 1; 383// int eleMag = 1; 384 laloSel.setNumLines(height); 385 laloSel.setNumEles(width); 386// laloSel.setBaseNumLines(height); 387// laloSel.setBaseNumElements(width); 388// laloSel.setLineMag(lineMag); 389// laloSel.setElementMag(eleMag); 390// laloSel.lineMagSlider.setValue(lineMag); 391// laloSel.setLRes(-1.0); 392// laloSel.elementMagSlider.setValue(eleMag); 393// laloSel.setERes(-1.0); 394// laloSel.amUpdating = true; 395// laloSel.lineMagSliderChanged(false); 396// laloSel.elementMagSliderChanged(false); 397// laloSel.amUpdating = false; 398// laloSel.setLockOn(lock); 399 400 laloSel.getGeoLocationInfo(line, ele); 401 String type = laloSel.getCoordinateType(); 402 int pos = 0; 403 if (laloSel.getPlace().equals(GeoLatLonSelection.PLACE_ULEFT)) pos = 1; 404 if (type.equals(GeoLatLonSelection.TYPE_LATLON)) { 405 double[][] pts = laloSel.getLatLonPoints(); 406 laloSel.setLatitude(pts[0][pos]); 407 laloSel.setLongitude(pts[1][pos]); 408 laloSel.convertToLineEle(); 409 } else { 410 double[][] pts = laloSel.getImagePoints(); 411 if (type.equals(GeoLatLonSelection.TYPE_AREA)) 412 pts = laloSel.getAreaPoints(); 413 laloSel.setElement((int)Math.floor(pts[0][pos] + 0.5)); 414 laloSel.setLine((int)Math.floor(pts[1][pos] + 0.5)); 415 laloSel.setLineElement(); 416 laloSel.convertToLatLon(); 417 } 418 } 419 } 420 421 @Override public void applyToDataSelection(DataSelection dataSelection) { 422 } 423 424 @Override public boolean getShowInControlProperties() { 425 return false; 426 } 427 428 public void drawBox() { 429 if (box == null) makeBox(); 430 removeRBB(); 431 432 double[][] elelin = laloSel.getDisplayELPoints(); 433 if (elelin == null) return; 434 435 for (int i=0; i<2; i++) { 436 for (int j=0; j<5; j++) { 437 Double val = Double.valueOf(elelin[i][j]); 438 if (val.isNaN()) { 439 eraseBox(); 440 return; 441 } 442 } 443 } 444 445 float[][] floatVals = new float[][] { 446 { (float)elelin[0][0], (float)elelin[0][1], (float)elelin[0][2], 447 (float)elelin[0][3], (float)elelin[0][4] }, 448 { (float)elelin[1][0], (float)elelin[1][1], (float)elelin[1][2], 449 (float)elelin[1][3], (float)elelin[1][4] }}; 450 451 float[][] dispVals = new float[][] { 452 { floatVals[0][1], floatVals[0][2], floatVals[0][4], 453 floatVals[0][3], floatVals[0][1] }, 454 { floatVals[1][1], floatVals[1][2], floatVals[1][4], 455 floatVals[1][3], floatVals[1][1] } 456 }; 457 458 try { 459 float[][] refVals = rbb.getDisplayCoordSystem().toReference(dispVals); 460 Gridded2DSet set = new Gridded2DSet(RealTupleType.SpatialCartesian2DTuple, 461 refVals, 5); 462 box.setData(set); 463 } catch (Exception e) { 464 logger.error("error drawing box", e); 465 } 466 } 467 468 private void makeBox() { 469 if (box == null) { 470 try { 471 box = new LineDrawing("box"); 472 box.setColor((Color)store.get(ViewManager.PREF_FGCOLOR, Color.GREEN)); 473 dspMaster.addDisplayable(box); 474 } catch (Exception e) { 475 logger.error("error making box", e); 476 } 477 } 478 } 479 480 private void eraseBox() { 481 Gridded2DSet set = null; 482 if (box == null) makeBox(); 483 try { 484 set = new Gridded2DSet(RealTupleType.LatitudeLongitudeTuple, 485 new float[][] { 486 { (float)0.0, (float)0.0 }, 487 { (float)0.0, (float)0.0 }, 488 }, 2); 489 box.setData(set); 490 } catch (Exception e) { 491 logger.error("error erasing box", e); 492 } 493 addRBB(); 494 } 495 496 private boolean rBBPresent() { 497 Displayable[] dsps = dspMaster.getDisplayables(); 498 if (dsps.length > 0) { 499 for (int i = 0; i < dsps.length; i++) { 500 Displayable disp = dsps[i]; 501 if (disp == (Displayable)rbb) { 502 return true; 503 } 504 } 505 } 506 return false; 507 } 508 509 private void removeRBB() { 510 if (rBBPresent()) { 511 try { 512 dspMaster.removeDisplayable(rbb); 513 } catch (Exception e) { 514 logger.error("error removing rubberband box", e); 515 } 516 } 517 addRBB(); 518 } 519 520 private void addRBB() { 521 if (!rBBPresent()) { 522 try { 523 dspMaster.addDisplayable(rbb); 524 } catch (Exception e) { 525 logger.error("error adding rubberband box", e); 526 } 527 } 528 } 529}