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 static javax.swing.GroupLayout.Alignment.BASELINE; 032import static javax.swing.GroupLayout.Alignment.LEADING; 033import static javax.swing.LayoutStyle.ComponentPlacement.RELATED; 034import static javax.swing.LayoutStyle.ComponentPlacement.UNRELATED; 035 036import java.awt.Dimension; 037import java.rmi.RemoteException; 038import java.time.ZonedDateTime; 039import java.time.format.DateTimeFormatter; 040import java.util.Calendar; 041import java.util.Date; 042import java.util.Hashtable; 043 044import javax.swing.GroupLayout; 045import javax.swing.JComponent; 046import javax.swing.JLabel; 047import javax.swing.JPanel; 048import javax.swing.JTextField; 049 050import name.gano.astro.time.Time; 051 052import org.slf4j.Logger; 053import org.slf4j.LoggerFactory; 054 055import ucar.unidata.data.DataSelection; 056import ucar.unidata.data.DataSelectionComponent; 057import ucar.unidata.data.DataSourceImpl; 058 059import visad.VisADException; 060 061import edu.wisc.ssec.mcidasv.Constants; 062import edu.wisc.ssec.mcidasv.data.dateChooser.JCalendar; 063import edu.wisc.ssec.mcidasv.data.dateChooser.JDateChooser; 064import edu.wisc.ssec.mcidasv.data.dateChooser.JDayChooser; 065import edu.wisc.ssec.mcidasv.data.dateChooser.JMonthChooser; 066import edu.wisc.ssec.mcidasv.data.dateChooser.JYearChooser; 067 068public class TimeRangeSelection extends DataSelectionComponent implements Constants { 069 070 private static final Logger logger = LoggerFactory.getLogger(TimeRangeSelection.class); 071 072 public static final String DEFAULT_BEGIN_TIME = "00:00:00"; 073 public static final String DEFAUULT_END_TIME = "23:59:59"; 074 075 private static Calendar cal = Calendar.getInstance(); 076 077 public static final Date DEFAULT_BEGIN_DATE = cal.getTime(); 078 public static final Date DEFAULT_END_DATE = cal.getTime(); 079 080 private JTextField beginTimeFld; 081 private JTextField endTimeFld; 082 083 private String selectedBeginTime = DEFAULT_BEGIN_TIME; 084 private String selectedEndTime = DEFAUULT_END_TIME; 085 086 private Date selectedBeginDate = DEFAULT_BEGIN_DATE; 087 private Date selectedEndDate = DEFAULT_END_DATE; 088 089 private JDateChooser begDay = null; 090 private JDateChooser endDay = null; 091 092 public static final String PROP_BEGTIME = "BeginTime"; 093 public static final String PROP_ENDTIME = "EndTime"; 094 public static final String PROP_BEGDATE = "BeginDate"; 095 public static final String PROP_ENDDATE = "EndDate"; 096 protected static final String PROP_BTIME = "BTime"; 097 protected static final String PROP_ETIME = "ETime"; 098 protected static final String PROP_YEAR = "Year"; 099 protected static final String PROP_MONTH = "Month"; 100 protected static final String PROP_DAY = "Day"; 101 protected static final String PROP_HOURS = "Hours"; 102 protected static final String PROP_MINS = "Mins"; 103 protected static final String PROP_SECS = "Secs"; 104 105 private JPanel timeRangeComp = new JPanel(); 106 107 public TimeRangeSelection(DataSourceImpl dataSource) 108 throws VisADException, RemoteException { 109 super("Time Range"); 110 } 111 112 protected JComponent doMakeContents() { 113 114 logger.debug("creating the TimeRangeSelection panel..."); 115 JLabel begLab = new JLabel(" Begin"); 116 JLabel endLab = new JLabel(" End"); 117 JLabel begTimeLab = new JLabel(" Time:"); 118 JLabel endTimeLab = new JLabel(" Time:"); 119 JLabel begDateLab = new JLabel(" Date:"); 120 JLabel endDateLab = new JLabel(" Date:"); 121 beginTimeFld = new JTextField(selectedBeginTime, 8); 122 beginTimeFld.setMaximumSize(new Dimension(80, 40)); 123 endTimeFld = new JTextField(selectedEndTime, 8); 124 endTimeFld.setMaximumSize(new Dimension(80, 40)); 125 begDay = new JDateChooser(selectedBeginDate); 126 begDay.setMaximumSize(new Dimension(140, 20)); 127 endDay = new JDateChooser(selectedEndDate); 128 endDay.setMaximumSize(new Dimension(140, 20)); 129 130 // make them listen to each other to maintain range validity (beg <= end) and vice versa 131 begDay.getJCalendar().getDayChooser().setName(JDayChooser.BEG_DAY); 132 endDay.getJCalendar().getDayChooser().setName(JDayChooser.END_DAY); 133 begDay.getJCalendar().getDayChooser().addPropertyChangeListener("day", endDay); 134 endDay.getJCalendar().getDayChooser().addPropertyChangeListener("day", begDay); 135 136 GroupLayout layout = new GroupLayout(timeRangeComp); 137 timeRangeComp.setLayout(layout); 138 layout.setHorizontalGroup( 139 layout.createParallelGroup(LEADING) 140 .addComponent(begLab) 141 .addGroup(layout.createSequentialGroup() 142 .addComponent(begTimeLab) 143 .addGap(GAP_RELATED) 144 .addComponent(beginTimeFld)) 145 .addGroup(layout.createSequentialGroup() 146 .addComponent(begDateLab) 147 .addGap(GAP_RELATED) 148 .addComponent(begDay)) 149 .addComponent(endLab) 150 .addGroup(layout.createSequentialGroup() 151 .addComponent(endTimeLab) 152 .addGap(GAP_RELATED) 153 .addComponent(endTimeFld)) 154 .addGroup(layout.createSequentialGroup() 155 .addComponent(endDateLab) 156 .addGap(GAP_RELATED) 157 .addComponent(endDay)) 158 ); 159 160 layout.setVerticalGroup( 161 layout.createParallelGroup(LEADING) 162 .addGroup(layout.createSequentialGroup() 163 .addComponent(begLab) 164 .addGroup(layout.createParallelGroup(BASELINE) 165 .addComponent(begTimeLab) 166 .addComponent(beginTimeFld)) 167 .addPreferredGap(RELATED) 168 .addGroup(layout.createParallelGroup(BASELINE) 169 .addComponent(begDateLab) 170 .addComponent(begDay)) 171 .addPreferredGap(UNRELATED) 172 .addComponent(endLab) 173 .addGroup(layout.createParallelGroup(BASELINE) 174 .addComponent(endTimeLab) 175 .addComponent(endTimeFld)) 176 .addPreferredGap(RELATED) 177 .addGroup(layout.createParallelGroup(BASELINE) 178 .addComponent(endDateLab) 179 .addComponent(endDay))) 180 ); 181 182 return timeRangeComp; 183 } 184 185 protected JComponent getTimeRangeComponent() { 186 return timeRangeComp; 187 } 188 189 public boolean begTimeOk() { 190 String begTime = beginTimeFld.getText(); 191 String[] timeStrings = begTime.split(":"); 192 int num = timeStrings.length; 193 if (num != 3) return false; 194 int hours = -1; 195 int mins = -1; 196 int secs = -1; 197 try { 198 hours = Integer.parseInt(timeStrings[0]); 199 mins = Integer.parseInt(timeStrings[1]); 200 secs = Integer.parseInt(timeStrings[2]); 201 } catch (NumberFormatException nfe) { 202 return false; 203 } 204 if ((hours < 0) || (hours > 23)) return false; 205 if ((mins < 0) || (mins > 59)) return false; 206 if ((secs < 0) || (secs > 59)) return false; 207 return true; 208 } 209 210 public boolean endTimeOk() { 211 String endTime = endTimeFld.getText(); 212 String[] timeStrings = endTime.split(":"); 213 int num = timeStrings.length; 214 if (num != 3) return false; 215 int hours = -1; 216 int mins = -1; 217 int secs = -1; 218 try { 219 hours = Integer.parseInt(timeStrings[0]); 220 mins = Integer.parseInt(timeStrings[1]); 221 secs = Integer.parseInt(timeStrings[2]); 222 } catch (NumberFormatException nfe) { 223 return false; 224 } 225 if ((hours < 0) || (hours > 23)) return false; 226 if ((mins < 0) || (mins > 59)) return false; 227 if ((secs < 0) || (secs > 59)) return false; 228 return true; 229 } 230 231 /** 232 * TJJ Jun 2015 233 * Return the difference in seconds between the Calendar objects only! 234 * NOTE: this will only give you the number of days difference, 235 * the HH, MM, and SS data comes from a different UI widget. 236 * 237 * We compute seconds simply because that is easiest way to get an 238 * absolute time from Date object 239 */ 240 241 public long getTimeRangeInSeconds() { 242 243 // make sure our widgets are set with a valid end time greater 244 // than begin time 245 if (! timeRangeOk()) { 246 return -1; 247 } 248 249 long endTimeMs = endDay.getDate().getTime(); 250 long begTimeMs = begDay.getDate().getTime(); 251 return (endTimeMs - begTimeMs) / 1000; 252 253 } 254 255 /** 256 * Make sure the end date/time exceeds the beginning date/time. 257 * 258 * @return true if condition met, false otherwise 259 * 260 */ 261 262 public boolean timeRangeOk() { 263 264 if (! begTimeOk()) return false; 265 if (! endTimeOk()) return false; 266 267 JCalendar cal = begDay.getJCalendar(); 268 JDayChooser dayChooser = cal.getDayChooser(); 269 int day = dayChooser.getDay(); 270 JMonthChooser monthChooser = cal.getMonthChooser(); 271 int month = monthChooser.getMonth() + 1; 272 JYearChooser yearChooser = cal.getYearChooser(); 273 int year = yearChooser.getYear(); 274 275 int hours = 0; 276 int mins = 0; 277 double secs = 0.0; 278 String begTime = beginTimeFld.getText(); 279 String[] timeStrings = begTime.split(":"); 280 int num = timeStrings.length; 281 if (num > 0) 282 hours = (Integer.parseInt(timeStrings[0])); 283 if (num > 1) 284 mins = (Integer.parseInt(timeStrings[1])); 285 if (num > 2) 286 secs = (Integer.parseInt(timeStrings[2])); 287 288 Time bTime = new Time(year, month, day, hours, mins, secs); 289 290 cal = endDay.getJCalendar(); 291 dayChooser = cal.getDayChooser(); 292 day = dayChooser.getDay(); 293 monthChooser = cal.getMonthChooser(); 294 month = monthChooser.getMonth() + 1; 295 yearChooser = cal.getYearChooser(); 296 year = yearChooser.getYear(); 297 298 String endTime = endTimeFld.getText(); 299 timeStrings = endTime.split(":"); 300 num = timeStrings.length; 301 if (num > 0) 302 hours = (Integer.parseInt(timeStrings[0])); 303 if (num > 1) 304 mins = (Integer.parseInt(timeStrings[1])); 305 if (num > 2) 306 secs = (Integer.parseInt(timeStrings[2])); 307 308 Time eTime = new Time(year, month, day, hours, mins, secs); 309 310 if (eTime.getJulianDate() < bTime.getJulianDate()) { 311 return false; 312 } 313 return true; 314 } 315 316 @Override public void applyToDataSelection(DataSelection dataSelection) { 317 318 if (! begTimeOk()) return; 319 if (! endTimeOk()) return; 320 321 if (dataSelection == null) { 322 dataSelection = new DataSelection(true); 323 } 324 325 JCalendar cal = begDay.getJCalendar(); 326 JDayChooser dayChooser = cal.getDayChooser(); 327 int day = dayChooser.getDay(); 328 JMonthChooser monthChooser = cal.getMonthChooser(); 329 int month = monthChooser.getMonth() + 1; 330 JYearChooser yearChooser = cal.getYearChooser(); 331 int year = yearChooser.getYear(); 332 333 int hours = 0; 334 int mins = 0; 335 double secs = 0.0; 336 String begTime = beginTimeFld.getText(); 337 String[] timeStrings = begTime.split(":"); 338 int num = timeStrings.length; 339 if (num > 0) 340 hours = (Integer.parseInt(timeStrings[0])); 341 if (num > 1) 342 mins = (Integer.parseInt(timeStrings[1])); 343 if (num > 2) 344 secs = (Integer.parseInt(timeStrings[2])); 345 if ((hours < 0) || (hours > 23)) hours = 0; 346 if ((mins < 0) || (mins > 59)) mins = 0; 347 if ((secs < 0.0) || (secs > 59.0)) secs = 0.0; 348 349 Time bTime = new Time(year, month, day, hours, mins, secs); 350 double dVal = bTime.getJulianDate(); 351 Double bigD = Double.valueOf(dVal); 352 String begTimeStr = bigD.toString(); 353 dataSelection.putProperty(PROP_BEGTIME, bTime.getDateTimeStr()); 354 355 Integer intVal = Integer.valueOf(year); 356 dataSelection.putProperty(PROP_YEAR, intVal.toString()); 357 intVal = Integer.valueOf(month); 358 dataSelection.putProperty(PROP_MONTH, intVal.toString()); 359 intVal = Integer.valueOf(day); 360 dataSelection.putProperty(PROP_DAY, intVal.toString()); 361 intVal = Integer.valueOf(hours); 362 dataSelection.putProperty(PROP_HOURS, intVal.toString()); 363 intVal = Integer.valueOf(mins); 364 dataSelection.putProperty(PROP_MINS, intVal.toString()); 365 Double doubleVal = Double.valueOf(secs); 366 dataSelection.putProperty(PROP_SECS, doubleVal.toString()); 367 368 cal = endDay.getJCalendar(); 369 dayChooser = cal.getDayChooser(); 370 day = dayChooser.getDay(); 371 monthChooser = cal.getMonthChooser(); 372 month = monthChooser.getMonth() + 1; 373 yearChooser = cal.getYearChooser(); 374 year = yearChooser.getYear(); 375 376 String endTime = endTimeFld.getText(); 377 timeStrings = endTime.split(":"); 378 num = timeStrings.length; 379 if (num > 0) 380 hours = (Integer.parseInt(timeStrings[0])); 381 if (num > 1) 382 mins = (Integer.parseInt(timeStrings[1])); 383 if (num > 2) 384 secs = (Integer.parseInt(timeStrings[2])); 385 if ((hours < 0) || (hours > 23)) hours = 0; 386 if ((mins < 0) || (mins > 59)) mins = 0; 387 if ((secs < 0.0) || (secs > 59.0)) secs = 0.0; 388 389 Time eTime = new Time(year, month, day, hours, mins, secs); 390 dVal = eTime.getJulianDate(); 391 bigD = Double.valueOf(dVal); 392 String endTimeStr = bigD.toString(); 393 394 dataSelection.putProperty(PROP_ENDTIME, eTime.getDateTimeStr()); 395 dataSelection.putProperty(PROP_BTIME, begTimeStr); 396 dataSelection.putProperty(PROP_ETIME, endTimeStr); 397 dataSelection.putProperty(PROP_BEGDATE, begDay.getDate()); 398 dataSelection.putProperty(PROP_ENDDATE, endDay.getDate()); 399 } 400 401 /** 402 * Applies {@link DataSelection} properties to this instance. 403 * 404 * @param props Properties from a {@code DataSelection}. 405 * Cannot be {@code null}. 406 */ 407 408 public void applyFromDataSelectionProperties(Hashtable<String, Object> props) { 409 410 if (props.containsKey(PROP_BEGTIME)) { 411 selectedBeginTime = extractTime((String)props.get(PROP_BEGTIME)); 412 if (beginTimeFld != null) { 413 beginTimeFld.setText(selectedBeginTime); 414 } 415 } 416 if (props.containsKey(PROP_ENDTIME)) { 417 selectedEndTime = extractTime((String)props.get(PROP_ENDTIME)); 418 if (endTimeFld != null) { 419 endTimeFld.setText(selectedEndTime); 420 } 421 } 422 if (props.containsKey(PROP_BEGDATE)) { 423 selectedBeginDate = ((Date) props.get(PROP_BEGDATE)); 424 if (begDay != null) { 425 begDay.setDate(selectedBeginDate); 426 } 427 } 428 if (props.containsKey(PROP_ENDDATE)) { 429 selectedEndDate = ((Date) props.get(PROP_ENDDATE)); 430 if (endDay != null) { 431 endDay.setDate(selectedEndDate); 432 } 433 } 434 435 } 436 437 /** 438 * Extract time of day from a string resembling 439 * {@literal "2019-03-18 13:59:59 UTC"}. 440 * 441 * @param dateTime String to extract from. Cannot be {@code null}. 442 * 443 * @return Time of day (UTC). 444 */ 445 446 private static String extractTime(String dateTime) { 447 DateTimeFormatter parser = 448 DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss z"); 449 450 DateTimeFormatter formatter = 451 DateTimeFormatter.ofPattern("HH:mm:ss"); 452 453 ZonedDateTime zdt = parser.parse(dateTime, ZonedDateTime::from); 454 455 return formatter.format(zdt); 456 } 457 458}