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.hydra; 030 031import java.awt.BorderLayout; 032import java.awt.Color; 033import java.awt.FlowLayout; 034import java.awt.event.ActionEvent; 035import java.awt.event.ActionListener; 036import java.net.URL; 037import java.rmi.RemoteException; 038import java.util.HashMap; 039import java.util.Hashtable; 040import java.util.Map; 041 042import javax.swing.DefaultBoundedRangeModel; 043import javax.swing.JComponent; 044import javax.swing.JLabel; 045import javax.swing.JPanel; 046import javax.swing.JSlider; 047import javax.swing.JTextField; 048import javax.swing.event.ChangeEvent; 049import javax.swing.event.ChangeListener; 050 051import org.slf4j.Logger; 052import org.slf4j.LoggerFactory; 053 054import ucar.unidata.data.DataChoice; 055import ucar.unidata.data.DataSelection; 056import ucar.unidata.data.DataSelectionComponent; 057import ucar.unidata.geoloc.ProjectionRect; 058import ucar.unidata.geoloc.projection.LatLonProjection; 059import ucar.unidata.view.geoloc.MapProjectionDisplayJ2D; 060import ucar.visad.ProjectionCoordinateSystem; 061import ucar.visad.display.DisplayMaster; 062import ucar.visad.display.LineDrawing; 063import ucar.visad.display.MapLines; 064 065import visad.FlatField; 066import visad.Gridded2DSet; 067import visad.GriddedSet; 068import visad.RealTupleType; 069import visad.VisADException; 070import visad.data.mcidas.BaseMapAdapter; 071import visad.georef.MapProjection; 072 073public class TrackSelection extends DataSelectionComponent { 074 075 private static final Logger logger = 076 LoggerFactory.getLogger(TrackSelection.class); 077 078 public static final int DEFAULT_TRACK_STRIDE = 5; 079 public static final int DEFAULT_VERTICAL_STRIDE = 2; 080 public static final int DEFAULT_TRACK_LENGTH_PERCENT = 5; 081 082 DataChoice dataChoice; 083 FlatField track; 084 085 double[] x_coords = new double[2]; 086 double[] y_coords = new double[2]; 087 MapProjectionDisplayJ2D mapProjDsp; 088 DisplayMaster dspMaster; 089 090 int trackStride; 091 int verticalStride; 092 093 JTextField trkStr; 094 JTextField vrtStr; 095 JTextField lengthField; 096 097 LineDrawing trackSelectDsp; 098 float[][] trackLocs; 099 int trackLen; 100 int trackPos; 101 int trackStart; 102 int trackStop; 103 int trackLengthPercent = DEFAULT_TRACK_LENGTH_PERCENT; 104 int selectWidth; 105 Map defaultSubset; 106 107 TrackSelection(DataChoice dataChoice, FlatField track, Map defaultSubset) 108 throws VisADException, RemoteException { 109 super("Track"); 110 this.dataChoice = dataChoice; 111 this.track = track; 112 this.defaultSubset = defaultSubset; 113 114 GriddedSet gset = (GriddedSet) track.getDomainSet(); 115 float[] lo = gset.getLow(); 116 float[] hi = gset.getHi(); 117 float[][] values = gset.getSamples(); 118 119 trackLen = values[0].length; 120 selectWidth = (int) (trackLen * ((float) trackLengthPercent / 100.0f)); 121 selectWidth /= 2; 122 trackPos = trackLen / 2; 123 trackStart = trackPos - selectWidth; 124 trackStop = trackPos + selectWidth; 125 126 trackLocs = values; 127 Gridded2DSet track2D = new Gridded2DSet(RealTupleType.SpatialEarth2DTuple, new float[][] { 128 values[0], values[1] }, values[0].length); 129 130 mapProjDsp = new MapProjectionDisplayJ2D(); 131 132 dspMaster = mapProjDsp; 133 mapProjDsp.setMapProjection(getDataProjection(new ProjectionRect(lo[0], lo[1], hi[0], hi[1]))); 134 LineDrawing trackDsp = new LineDrawing("track"); 135 trackDsp.setLineWidth(0.5f); 136 trackDsp.setData(track2D); 137 138 trackSelectDsp = new LineDrawing("trackSelect"); 139 trackSelectDsp.setLineWidth(3f); 140 trackSelectDsp.setColor(java.awt.Color.magenta); 141 142 updateTrackSelect(); 143 144 mapProjDsp.addDisplayable(trackSelectDsp); 145 mapProjDsp.addDisplayable(trackDsp); 146 147 MapLines mapLines = new MapLines("maplines"); 148 URL mapSource = mapProjDsp.getClass().getResource("/auxdata/maps/OUTLSUPU"); 149 try { 150 BaseMapAdapter mapAdapter = new BaseMapAdapter(mapSource); 151 mapLines.setMapLines(mapAdapter.getData()); 152 mapLines.setColor(Color.cyan); 153 } catch (Exception excp) { 154 logger.error("cannot open map file: " + mapSource, excp); 155 } 156 157 mapLines = new MapLines("maplines"); 158 mapSource = mapProjDsp.getClass().getResource("/auxdata/maps/OUTLSUPW"); 159 try { 160 BaseMapAdapter mapAdapter = new BaseMapAdapter(mapSource); 161 mapLines.setMapLines(mapAdapter.getData()); 162 mapLines.setColor(Color.cyan); 163 mapProjDsp.addDisplayable(mapLines); 164 } catch (Exception excp) { 165 logger.error("cannot open map file: " + mapSource, excp); 166 } 167 168 mapLines = new MapLines("maplines"); 169 mapSource = mapProjDsp.getClass().getResource("/auxdata/maps/OUTLHPOL"); 170 try { 171 BaseMapAdapter mapAdapter = new BaseMapAdapter(mapSource); 172 mapLines.setMapLines(mapAdapter.getData()); 173 mapLines.setColor(Color.cyan); 174 } catch (Exception excp) { 175 logger.error("cannot open map file: " + mapSource, excp); 176 } 177 178 dspMaster.draw(); 179 } 180 181 public MapProjection getDataProjection(ProjectionRect rect) { 182 MapProjection mp = null; 183 try { 184 mp = new ProjectionCoordinateSystem(new LatLonProjection("blah", rect)); 185 } catch (Exception e) { 186 logger.error("error getting data projection", e); 187 } 188 return mp; 189 } 190 191 protected JComponent doMakeContents() { 192 try { 193 JPanel panel = new JPanel(new BorderLayout()); 194 panel.add("Center", dspMaster.getDisplayComponent()); 195 196 JPanel stridePanel = new JPanel(new FlowLayout()); 197 198 // initialize the UI components 199 trkStr = new JTextField(Integer.toString(TrackSelection.DEFAULT_TRACK_STRIDE), 2); 200 vrtStr = new JTextField(Integer.toString(TrackSelection.DEFAULT_VERTICAL_STRIDE), 2); 201 lengthField = new JTextField(Integer.toString(TrackSelection.DEFAULT_TRACK_LENGTH_PERCENT), 2); 202 203 // set tooltip hints 204 trkStr.setToolTipText("Sets the horizontal stride along the track (X/Y-axes)"); 205 vrtStr.setToolTipText("Sets the vertical stride (Z-axis)"); 206 lengthField.setToolTipText("Sets the percentage length of total track to display"); 207 208 trkStr.addActionListener(new ActionListener() { 209 public void actionPerformed(ActionEvent ae) { 210 setTrackStride(Integer.valueOf(trkStr.getText().trim())); 211 } 212 }); 213 vrtStr.addActionListener(new ActionListener() { 214 public void actionPerformed(ActionEvent ae) { 215 setVerticalStride(Integer.valueOf(vrtStr.getText().trim())); 216 } 217 }); 218 lengthField.addActionListener(new ActionListener() { 219 public void actionPerformed(ActionEvent ae) { 220 setLengthPercent(Integer.valueOf(lengthField.getText().trim())); 221 } 222 }); 223 224 stridePanel.add(new JLabel("Track Stride:")); 225 stridePanel.add(trkStr); 226 stridePanel.add(new JLabel("Vertical Stride:")); 227 stridePanel.add(vrtStr); 228 stridePanel.add(new JLabel("Length %:")); 229 stridePanel.add(lengthField); 230 231 JPanel selectPanel = new JPanel(new BorderLayout()); 232 DefaultBoundedRangeModel brm = new DefaultBoundedRangeModel(trackStart, 0, 0, trackLen); 233 JSlider trackSelect = new JSlider(brm); 234 trackSelect 235 .setToolTipText("<html>" 236 + "Sets the location of the track to display (with respect to Length % below). <br>" 237 + "The slider represents the middle of the length to be plotted. The left end of <br>" 238 + "the slider is the beginning of the track, and the right is the end. The portion <br>" 239 + "of the track to be displayed is outlined in magenta." + "</html>"); 240 Hashtable<Integer, JLabel> labelTable = new Hashtable<Integer, JLabel>(); 241 labelTable.put(0, new JLabel("Track Start")); 242 labelTable.put(trackLen, new JLabel("Track End")); 243 trackSelect.setLabelTable(labelTable); 244 trackSelect.setPaintLabels(true); 245 trackSelect.addChangeListener(new ChangeListener() { 246 public void stateChanged(ChangeEvent e) { 247 trackPos = (int) ((JSlider) e.getSource()).getValue(); 248 updateTrackSelect(); 249 } 250 }); 251 selectPanel.add(trackSelect, BorderLayout.NORTH); 252 selectPanel.add(stridePanel, BorderLayout.SOUTH); 253 panel.add(selectPanel, BorderLayout.SOUTH); 254 255 return panel; 256 } catch (Exception e) { 257 logger.error("error creating contents", e); 258 } 259 return null; 260 } 261 262 public void setTrackStride(int stride) { 263 trackStride = stride; 264 } 265 266 public void setVerticalStride(int stride) { 267 verticalStride = stride; 268 } 269 270 public void setLengthPercent(int percent) { 271 trackLengthPercent = percent; 272 selectWidth = (int) (trackLen * ((float) trackLengthPercent / 100.0f)); 273 selectWidth /= 2; 274 updateTrackSelect(); 275 } 276 277 /** 278 * Update Track Stride if input text box holds a positive integer. 279 * 280 * @return true if trackStride was updated 281 */ 282 283 public boolean setTrackStride() { 284 boolean setOk = false; 285 try { 286 int newStride = Integer.valueOf(trkStr.getText().trim()); 287 if (newStride >= 1) { 288 trackStride = newStride; 289 setOk = true; 290 } else { 291 setOk = false; 292 } 293 } catch (NumberFormatException nfe) { 294 // do nothing, will return correct result code 295 } 296 return setOk; 297 } 298 299 /** 300 * Update Vertical Stride if input text box holds a positive integer. 301 * 302 * @return true if verticalStride was updated 303 */ 304 305 public boolean setVerticalStride() { 306 boolean setOk = false; 307 try { 308 int newStride = Integer.valueOf(vrtStr.getText().trim()); 309 if (newStride >= 1) { 310 verticalStride = newStride; 311 setOk = true; 312 } else { 313 setOk = false; 314 } 315 } catch (NumberFormatException nfe) { 316 // do nothing, will return correct result code 317 } 318 return setOk; 319 } 320 321 /** 322 * Update Track Length percentage if input text box holds a positive integer 323 * between 1 and 100. 324 * 325 * @return true if trackLengthPercent was updated 326 */ 327 328 public boolean setLengthPercent() { 329 boolean setOk = false; 330 try { 331 int newWidth = Integer.valueOf(lengthField.getText().trim()); 332 if ((newWidth > 0) && (newWidth <= 100)) { 333 trackLengthPercent = newWidth; 334 setOk = true; 335 } 336 } catch (NumberFormatException nfe) { 337 // do nothing, will return correct result code 338 } 339 return setOk; 340 } 341 342 void updateTrackSelect() { 343 trackStart = trackPos - selectWidth; 344 if (trackStart < 0) 345 trackStart = 0; 346 347 trackStop = trackPos + selectWidth; 348 if (trackStop >= trackLen) 349 trackStop = trackLen - 1; 350 351 try { 352 Gridded2DSet trck = new Gridded2DSet(RealTupleType.SpatialEarth2DTuple, new float[][] { 353 java.util.Arrays.copyOfRange(trackLocs[0], trackStart, trackStop), 354 java.util.Arrays.copyOfRange(trackLocs[1], trackStart, trackStop) }, 355 (trackStop - trackStart)); 356 trackSelectDsp.setData(trck); 357 } catch (Exception exc) { 358 logger.trace("Problem creating Gridded2DSet", exc); 359 } 360 } 361 362 /** 363 * Apply new settings 364 */ 365 366 public void applyToDataSelection(DataSelection dataSelection) { 367 setTrackStride(); 368 setVerticalStride(); 369 setLengthPercent(); 370 371 HashMap subset = (HashMap) ((HashMap) defaultSubset).clone(); 372 double[] coords = (double[]) subset.get(ProfileAlongTrack.vertDim_name); 373 374 subset.put(ProfileAlongTrack.trackDim_name, new double[] { trackStart, trackStop, 375 trackStride }); 376 subset.put(ProfileAlongTrack.vertDim_name, new double[] { coords[0], coords[1], 377 verticalStride }); 378 379 MultiDimensionSubset select = new MultiDimensionSubset(subset); 380 381 Hashtable table = dataChoice.getProperties(); 382 table.put(MultiDimensionSubset.key, select); 383 384 table = dataSelection.getProperties(); 385 table.put(MultiDimensionSubset.key, select); 386 387 dataChoice.setDataSelection(dataSelection); 388 389 } 390 391}