PageRenderTime 29ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/src/main/java/org/mbari/dss/client/views/PlaybackView.java

http://mbari-dss.googlecode.com/
Java | 647 lines | 483 code | 97 blank | 67 comment | 34 complexity | fd2c05c39241445495342a9a15e16cbd MD5 | raw file
  1. /*
  2. * @(#)PlaybackView.java
  3. *
  4. * Copyright 2011 MBARI
  5. *
  6. * Permission is hereby granted, free of charge, to any person obtaining a copy
  7. * of this software and associated documentation files (the "Software"), to deal
  8. * in the Software without restriction, including without limitation the rights
  9. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10. * copies of the Software, and to permit persons to whom the Software is
  11. * furnished to do so, subject to the following conditions:
  12. *
  13. * The above copyright notice and this permission notice shall be included in
  14. * all copies or substantial portions of the Software.
  15. *
  16. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22. * THE SOFTWARE.
  23. */
  24. package org.mbari.dss.client.views;
  25. //~--- non-JDK imports --------------------------------------------------------
  26. import com.allen_sauer.gwt.log.client.Log;
  27. import com.google.gwt.user.client.Timer;
  28. import com.google.gwt.user.client.ui.Widget;
  29. import com.smartgwt.client.data.DateRange;
  30. import com.smartgwt.client.types.Alignment;
  31. import com.smartgwt.client.widgets.IButton;
  32. import com.smartgwt.client.widgets.Label;
  33. import com.smartgwt.client.widgets.Progressbar;
  34. import com.smartgwt.client.widgets.Slider;
  35. import com.smartgwt.client.widgets.events.ClickEvent;
  36. import com.smartgwt.client.widgets.events.ClickHandler;
  37. import com.smartgwt.client.widgets.events.ValueChangedEvent;
  38. import com.smartgwt.client.widgets.events.ValueChangedHandler;
  39. import com.smartgwt.client.widgets.form.DynamicForm;
  40. import com.smartgwt.client.widgets.form.fields.DateItem;
  41. import com.smartgwt.client.widgets.form.fields.TimeItem;
  42. import com.smartgwt.client.widgets.form.fields.events.ChangeEvent;
  43. import com.smartgwt.client.widgets.form.fields.events.ChangeHandler;
  44. import com.smartgwt.client.widgets.form.fields.events.ChangedEvent;
  45. import com.smartgwt.client.widgets.form.fields.events.ChangedHandler;
  46. import com.smartgwt.client.widgets.layout.HLayout;
  47. import com.smartgwt.client.widgets.layout.VLayout;
  48. import org.mbari.dss.client.data.DataUtil;
  49. import org.mbari.dss.client.data.DssTimer;
  50. import org.mbari.dss.client.events.EventFirer;
  51. import org.mbari.dss.client.events.PlaybackTimerEvent;
  52. //~--- JDK imports ------------------------------------------------------------
  53. import java.util.Date;
  54. /**
  55. * To playback the specified date range of platform tracks
  56. *
  57. * For some ideas on animation playback in OL, see
  58. * http://osgeo-org.1803224.n2.nabble.com/Has-anyone-tried-animation-or-how-to-td1829343.html
  59. *
  60. * @author dcline
  61. */
  62. public class PlaybackView implements DssView {
  63. private int frameDelaySecs = 10;
  64. private int progressDelayMSec = 0;
  65. private PlaybackTimer timer = new PlaybackTimer();
  66. private State state = State.LOAD_STOP;
  67. private SliderDateLabel startSliderLabel = new SliderDateLabel();
  68. private SliderDateLabel endSliderLabel = new SliderDateLabel();
  69. private PlaybackDateLabel currentDateLabel = new PlaybackDateLabel();
  70. private int updateMin = 60; // 1 hour
  71. private DateRange selectedRange = new DateRange();
  72. private DateRange playbackRange = new DateRange();
  73. private DateRange loadedRange = new DateRange();
  74. private final Slider endSlider, startSlider;
  75. private FrameDelaySelectItem frameDelaySelect;
  76. private final IButton playBtn, loadBtn, resetBtn;
  77. private final Progressbar progressBar;
  78. private Label progressBarLabel;
  79. private Timer progressBarTimer;
  80. private int progressBarValue;
  81. private CustomSelectItem updateSelect;
  82. private final VLayout vLayout;
  83. public enum State { LOAD_STOP, LOAD_START, LOAD_FINI }
  84. public PlaybackView() {
  85. progressBar = new Progressbar();
  86. progressBar.setHeight(24);
  87. progressBar.setWidth(150);
  88. progressBar.setVertical(false);
  89. progressBarTimer = _createProgressBarTimer();
  90. progressBarLabel = new Label("Current Loading Progress:");
  91. progressBarLabel.setHeight(16);
  92. endSlider = new DateSlider("End Date");
  93. startSlider = new DateSlider("Start Date");
  94. startSlider.addValueChangedHandler(new ValueChangedHandler() {
  95. public void onValueChanged(ValueChangedEvent event) {
  96. int timeStepMinutes = event.getValue();
  97. long totalSeconds = loadedRange.getStartDate().getTime() + ((long) timeStepMinutes) * 60000L;
  98. Date startDate = new Date(totalSeconds);
  99. startSliderLabel._formatLabel(new Date(totalSeconds).toString());
  100. timeStepMinutes = (int) endSlider.getValue();
  101. totalSeconds = loadedRange.getStartDate().getTime() + ((long) timeStepMinutes) * 60000L;
  102. Date endDate = new Date(totalSeconds);
  103. playbackRange.setStartDate(startDate);
  104. playbackRange.setEndDate(endDate);
  105. if (state == State.LOAD_FINI) {
  106. EventFirer.fireEvent(new PlaybackTimerEvent(playbackRange,
  107. PlaybackTimerEvent.Event.PLAYBACK_RANGE_CHANGED));
  108. }
  109. }
  110. });
  111. endSlider.addValueChangedHandler(new ValueChangedHandler() {
  112. public void onValueChanged(ValueChangedEvent event) {
  113. int timeStepMinutes = event.getValue();
  114. long totalSeconds = loadedRange.getStartDate().getTime() + ((long) timeStepMinutes) * 60000L;
  115. Date endDate = new Date(totalSeconds);
  116. endSliderLabel._formatLabel(new Date(totalSeconds).toString());
  117. timeStepMinutes = (int) startSlider.getValue();
  118. totalSeconds = loadedRange.getStartDate().getTime() + ((long) timeStepMinutes) * 60000L;
  119. Date startDate = new Date(totalSeconds);
  120. playbackRange.setStartDate(startDate);
  121. playbackRange.setEndDate(endDate);
  122. if (state == State.LOAD_FINI) {
  123. EventFirer.fireEvent(new PlaybackTimerEvent(playbackRange,
  124. PlaybackTimerEvent.Event.PLAYBACK_RANGE_CHANGED));
  125. }
  126. }
  127. });
  128. selectedRange = DataUtil.getInitialDateRange();
  129. playbackRange.setStartDate(new Date(selectedRange.getStartDate().getTime()));
  130. playbackRange.setEndDate(new Date(selectedRange.getEndDate().getTime()));
  131. loadedRange.setStartDate(new Date(selectedRange.getStartDate().getTime()));
  132. loadedRange.setEndDate(new Date(selectedRange.getEndDate().getTime()));
  133. // TODO: implement client-side to parse out PDT/PST from
  134. // date since the 3-letter format isn't support in the GWT toolkit
  135. String timezone = "PDT";
  136. DateItem startDateItem = new DateItem();
  137. startDateItem.setName("Start");
  138. startDateItem.setTitle("Start" + "(" + timezone + ")");
  139. startDateItem.setValue(selectedRange.getStartDate());
  140. startDateItem.addChangedHandler(_createStartDateItemChangeHandler());
  141. TimeItem startTimeItem = new TimeItem("timeItem", "Time");
  142. startTimeItem.setUseMask(true);
  143. startTimeItem.setValue(selectedRange.getStartDate());
  144. startTimeItem.addChangedHandler(_createStartTimeItemChangeHandler());
  145. DateItem endDateItem = new DateItem();
  146. endDateItem.setName("End");
  147. endDateItem.setTitle("End" + "(" + timezone + ")");
  148. endDateItem.setValue(selectedRange.getEndDate());
  149. endDateItem.addChangedHandler(_createEndDateItemChangeHandler());
  150. TimeItem endTimeItem = new TimeItem("timeItem", "Time");
  151. endTimeItem.setUseMask(true);
  152. endTimeItem.setValue(selectedRange.getEndDate());
  153. endTimeItem.addChangedHandler(_createEndTimeItemChangeHandler());
  154. _initSliders();
  155. playBtn = new IButton("Animate Map");
  156. playBtn.setShowRollOver(true);
  157. playBtn.setShowDisabled(true);
  158. playBtn.setShowDown(true);
  159. playBtn.setTitle("Animate Map");
  160. playBtn.addClickHandler(_createPlayClickHandler());
  161. loadBtn = new IButton("Load");
  162. loadBtn.setWidth(150);
  163. loadBtn.setShowRollOver(true);
  164. loadBtn.setShowDisabled(true);
  165. loadBtn.setShowDown(true);
  166. loadBtn.setTitle("Load");
  167. loadBtn.addClickHandler(_createLoadClickHandler());
  168. resetBtn = new IButton("Load");
  169. resetBtn.setWidth(150);
  170. resetBtn.setShowRollOver(true);
  171. resetBtn.setShowDisabled(true);
  172. resetBtn.setShowDown(true);
  173. resetBtn.setTitle("Reset");
  174. resetBtn.addClickHandler(_createResetClickHandler());
  175. HLayout progressCanvas = new HLayout();
  176. progressCanvas.setWidth100();
  177. progressCanvas.setAlign(Alignment.CENTER);
  178. progressCanvas.addMember(progressBarLabel);
  179. progressCanvas.addMember(progressBar);
  180. DynamicForm form = new DynamicForm();
  181. form.setNumCols(3);
  182. form.setAlign(Alignment.LEFT);
  183. form.setWidth100();
  184. form.setFields(startDateItem, startTimeItem, endDateItem, endTimeItem);
  185. HLayout buttonCanvasLoad = new HLayout();
  186. buttonCanvasLoad.setAlign(Alignment.CENTER);
  187. loadBtn.setWidth(125);
  188. loadBtn.setTooltip("Loads sensor and platform tracks for selected dates");
  189. buttonCanvasLoad.addMember(loadBtn);
  190. HLayout buttonCanvasPlay = new HLayout();
  191. buttonCanvasPlay.setAlign(Alignment.CENTER);
  192. playBtn.setWidth(125);
  193. playBtn.setTooltip("Plays animation");
  194. buttonCanvasPlay.addMember(playBtn);
  195. HLayout resetCanvasPlay = new HLayout();
  196. resetCanvasPlay.setAlign(Alignment.CENTER);
  197. resetBtn.setWidth(125);
  198. resetBtn.setTooltip("Resets map back to real-time updates");
  199. resetCanvasPlay.addMember(resetBtn);
  200. vLayout = new VLayout();
  201. vLayout.setMembersMargin(5);
  202. vLayout.setLayoutMargin(10);
  203. vLayout.setAlign(Alignment.CENTER);
  204. vLayout.setAutoHeight();
  205. vLayout.addMember(form);
  206. vLayout.addMember(endSliderLabel);
  207. vLayout.addMember(endSlider);
  208. vLayout.addMember(startSliderLabel);
  209. vLayout.addMember(startSlider);
  210. vLayout.addMember(currentDateLabel);
  211. vLayout.addMember(progressCanvas);
  212. vLayout.addMember(buttonCanvasLoad);
  213. vLayout.addMember(buttonCanvasPlay);
  214. vLayout.addMember(resetCanvasPlay);
  215. vLayout.addMember(_createFrameDelayForm());
  216. vLayout.addMember(_createUpdateSelectForm());
  217. vLayout.setHeight(50);
  218. _setState(State.LOAD_STOP);
  219. }
  220. private void _initSliders() {
  221. if (state != State.LOAD_FINI) {
  222. // initialize the timer slider to the total duration in seconds
  223. float maxTimeMins = (float) (selectedRange.getEndDate().getTime() - selectedRange.getStartDate().getTime())
  224. / 60000L;
  225. endSlider.setMinValue(0);
  226. endSlider.setMaxValue(maxTimeMins);
  227. endSlider.setValue(maxTimeMins);
  228. endSliderLabel._formatLabel(selectedRange.getEndDate().toString());
  229. startSlider.setMinValue(0);
  230. startSlider.setMaxValue(maxTimeMins);
  231. startSlider.setValue(0);
  232. startSliderLabel._formatLabel(selectedRange.getStartDate().toString());
  233. }
  234. }
  235. private DynamicForm _createFrameDelayForm() {
  236. final DynamicForm form = new DynamicForm();
  237. form.setNumCols(3);
  238. frameDelaySelect = new FrameDelaySelectItem("Frame Delay");
  239. frameDelaySelect.setValue("10 sec");
  240. frameDelaySelect.setTooltip("Frame rate for animation");
  241. frameDelaySelect.addChangeHandler(new ChangeHandler() {
  242. @Override
  243. public void onChange(ChangeEvent event) {
  244. try {
  245. String value = frameDelaySelect.getString2SecsDict((String) event.getValue());
  246. frameDelaySecs = Integer.parseInt(value);
  247. } catch (Exception e) {
  248. // TODO: log exception
  249. }
  250. }
  251. });
  252. form.setFields(frameDelaySelect);
  253. return form;
  254. }
  255. private DynamicForm _createUpdateSelectForm() {
  256. final DynamicForm form = new DynamicForm();
  257. form.setNumCols(3);
  258. updateSelect = new CustomSelectItem("Display");
  259. updateSelect.setValue("1 hour");
  260. updateSelect.setTooltip("Duration to display each frame during animation playback");
  261. updateSelect.addChangeHandler(new ChangeHandler() {
  262. @Override
  263. public void onChange(ChangeEvent event) {
  264. try {
  265. String value = updateSelect.getDayName2MinsDict((String) event.getValue());
  266. updateMin = Integer.parseInt(value);
  267. } catch (Exception e) {
  268. // TODO: log exception
  269. }
  270. }
  271. });
  272. form.setFields(updateSelect);
  273. return form;
  274. }
  275. /**
  276. * Updates the playback label with the string
  277. *
  278. * @param string
  279. */
  280. public void updateText(String string) {
  281. currentDateLabel._formatLabel(string);
  282. }
  283. /**
  284. * Return the date range
  285. *
  286. * @return
  287. */
  288. public DateRange getDateRange() {
  289. return selectedRange;
  290. }
  291. public void resetPlayButton(boolean state) {
  292. if (state == true) {
  293. playBtn.setTitle("Animate Map");
  294. } else {
  295. playBtn.setTitle("Stop Animation");
  296. }
  297. }
  298. void reset() {
  299. _setState(State.LOAD_STOP);
  300. }
  301. private void playPause() {
  302. if (playBtn.getTitle().equals("Animate Map")) {
  303. resetPlayButton(false);
  304. timer.changeRange(playbackRange);
  305. timer.changeDisplayMins(updateMin);
  306. timer.changeTimerPeriodSeconds(frameDelaySecs);
  307. timer.start();
  308. } else {
  309. resetPlayButton(true);
  310. timer.stop();
  311. }
  312. }
  313. private ClickHandler _createResetClickHandler() {
  314. return new ClickHandler() {
  315. @Override
  316. public void onClick(ClickEvent event) {
  317. _setState(State.LOAD_STOP);
  318. DssTimer.getInstance().init();
  319. }
  320. };
  321. }
  322. private ClickHandler _createPlayClickHandler() {
  323. return new ClickHandler() {
  324. @Override
  325. public void onClick(ClickEvent event) {
  326. playPause();
  327. }
  328. };
  329. }
  330. private ClickHandler _createLoadClickHandler() {
  331. return new ClickHandler() {
  332. @Override
  333. public void onClick(ClickEvent event) {
  334. int timeStepMins = (int) ((selectedRange.getEndDate().getTime()
  335. - selectedRange.getStartDate().getTime()) / 60000L);
  336. if (state == State.LOAD_START) {
  337. _setState(State.LOAD_STOP);
  338. } else {
  339. _setState(State.LOAD_START);
  340. DssTimer.getInstance().setPlayback(selectedRange, timeStepMins, timeStepMins,
  341. DssTimer.Mode.PLAYBACK);
  342. }
  343. }
  344. };
  345. }
  346. public State getState() {
  347. return this.state;
  348. }
  349. private void _setState(State newState) {
  350. this.state = newState;
  351. if (newState == State.LOAD_START) {
  352. frameDelaySelect.disable();
  353. updateSelect.disable();
  354. endSlider.disable();
  355. startSlider.disable();
  356. playBtn.disable();
  357. loadBtn.setTitle("Stop Loading");
  358. progressBar.setVisible(true);
  359. progressBarLabel.setVisible(true);
  360. progressBarValue = 0;
  361. progressDelayMSec = (int) ((playbackRange.getEndDate().getTime() - playbackRange.getStartDate().getTime())
  362. / 60000L);
  363. progressBarTimer.schedule(50);
  364. } else if (newState == State.LOAD_FINI) {
  365. loadedRange.setStartDate(new Date(selectedRange.getStartDate().getTime()));
  366. loadedRange.setEndDate(new Date(selectedRange.getEndDate().getTime()));
  367. playbackRange.setStartDate(new Date(selectedRange.getStartDate().getTime()));
  368. playbackRange.setEndDate(new Date(selectedRange.getEndDate().getTime()));
  369. frameDelaySelect.enable();
  370. updateSelect.enable();
  371. endSlider.enable();
  372. startSlider.enable();
  373. playBtn.enable();
  374. loadBtn.setTitle("Load");
  375. progressBarValue = 100;
  376. progressBar.setVisible(false);
  377. progressBarLabel.setVisible(false);
  378. } else if (newState == State.LOAD_STOP) {
  379. frameDelaySelect.disable();
  380. updateSelect.disable();
  381. endSlider.disable();
  382. startSlider.disable();
  383. playBtn.disable();
  384. loadBtn.setTitle("Load");
  385. loadBtn.enable();
  386. progressBarValue = 0;
  387. progressBarTimer.cancel();
  388. progressBar.setVisible(false);
  389. progressBarLabel.setVisible(false);
  390. endSlider.disable();
  391. startSlider.disable();
  392. playBtn.disable();
  393. playBtn.setTitle("Animate Map");
  394. }
  395. }
  396. /**
  397. * Helper method to reset the load button visibility if selected
  398. * a date outside the loaded range
  399. *
  400. * @param date
  401. */
  402. private void checkRange(Date date) {
  403. if (state == State.LOAD_FINI) {
  404. Date start = loadedRange.getStartDate();
  405. Date end = loadedRange.getEndDate();
  406. if ((date.getTime() < start.getTime())
  407. || (date.getTime() > end.getTime())) {
  408. loadBtn.enable();
  409. }
  410. }
  411. }
  412. void handleNavDataRetrieved() {
  413. if (state.equals(State.LOAD_START)) {
  414. _setState(State.LOAD_FINI);
  415. }
  416. }
  417. private Timer _createProgressBarTimer() {
  418. return new Timer() {
  419. public void run() {
  420. progressBarValue += 1 + (int) (10 * Math.random());
  421. if (progressBarValue >= 100) {
  422. if (state != State.LOAD_FINI) {
  423. progressBarValue = 0;
  424. } else {
  425. progressBarValue = 100;
  426. progressBarLabel.setContents("Loading Complete");
  427. }
  428. }
  429. progressBar.setPercentDone(progressBarValue);
  430. progressBarLabel.setContents("Loading Progress: " + progressBarValue + "%");
  431. if (progressBarValue != 100) {
  432. schedule(progressDelayMSec + (int) (50 * Math.random()));
  433. } else {
  434. progressBar.setVisible(false);
  435. progressBarLabel.setVisible(false);
  436. return;
  437. }
  438. }
  439. };
  440. }
  441. private ChangedHandler _createEndDateItemChangeHandler() {
  442. return new ChangedHandler() {
  443. @Override
  444. public void onChanged(ChangedEvent event) {
  445. Date date = (Date) event.getValue();
  446. Date endDate = selectedRange.getEndDate();
  447. endDate.setMonth(date.getMonth());
  448. endDate.setDate(date.getDate());
  449. endDate.setYear(date.getYear());
  450. checkRange(endDate);
  451. selectedRange.setEndDate(endDate);
  452. _initSliders();
  453. }
  454. };
  455. }
  456. private ChangedHandler _createEndTimeItemChangeHandler() {
  457. return new ChangedHandler() {
  458. @Override
  459. public void onChanged(ChangedEvent event) {
  460. Date date = (Date) event.getValue();
  461. Date endDate = selectedRange.getEndDate();
  462. endDate.setHours(date.getHours());
  463. endDate.setMinutes(date.getMinutes());
  464. endDate.setSeconds(date.getSeconds());
  465. selectedRange.setEndDate(endDate);
  466. checkRange(endDate);
  467. _initSliders();
  468. }
  469. };
  470. }
  471. private ChangedHandler _createStartDateItemChangeHandler() {
  472. return new ChangedHandler() {
  473. @Override
  474. public void onChanged(ChangedEvent event) {
  475. Date date = (Date) event.getValue();
  476. Date startDate = selectedRange.getStartDate();
  477. startDate.setMonth(date.getMonth());
  478. startDate.setDate(date.getDate());
  479. startDate.setYear(date.getYear());
  480. selectedRange.setStartDate(startDate);
  481. checkRange(startDate);
  482. _initSliders();
  483. }
  484. };
  485. }
  486. private ChangedHandler _createStartTimeItemChangeHandler() {
  487. return new ChangedHandler() {
  488. @Override
  489. public void onChanged(ChangedEvent event) {
  490. Date date = (Date) event.getValue();
  491. Date startDate = selectedRange.getStartDate();
  492. startDate.setHours(date.getHours());
  493. startDate.setMinutes(date.getMinutes());
  494. startDate.setSeconds(date.getSeconds());
  495. selectedRange.setStartDate(startDate);
  496. checkRange(startDate);
  497. _initSliders();
  498. }
  499. };
  500. }
  501. /**
  502. * Gets the type of the instance.
  503. * @return the {@link DssViewKey}
  504. */
  505. public DssViewKey getType() {
  506. return DssViewKey.PLAYBACK_VIEW;
  507. }
  508. public Widget getWidget() {
  509. return vLayout;
  510. }
  511. /**
  512. * Date slider for controller start/end dates
  513. */
  514. class DateSlider extends Slider {
  515. public DateSlider(String title) {
  516. this.setWidth(250);
  517. this.setVertical(false);
  518. this.setStepPercent(5.0f);
  519. this.setTitle(title);
  520. this.setShowRange(false);
  521. this.setShowValue(false);
  522. }
  523. }
  524. /**
  525. * Label for displaying playback date and time
  526. */
  527. class PlaybackDateLabel extends Label {
  528. public PlaybackDateLabel() {
  529. setAlign(Alignment.LEFT);
  530. setHeight(40);
  531. setWidth100();
  532. }
  533. private void _formatLabel(String label) {
  534. setContents("<b>Playback Time</b><br>" + label);
  535. }
  536. }
  537. /**
  538. * Label for displaying start/end date and time
  539. */
  540. class SliderDateLabel extends Label {
  541. public SliderDateLabel() {
  542. setAlign(Alignment.LEFT);
  543. setHeight(40);
  544. setWidth100();
  545. }
  546. private void _formatLabel(String label) {
  547. setContents(label);
  548. }
  549. }
  550. }