001 package org.hackystat.projectbrowser.page.trajectory.datapanel; 002 003 import java.io.Serializable; 004 import java.text.DecimalFormat; 005 import java.text.NumberFormat; 006 import java.text.SimpleDateFormat; 007 import java.util.ArrayList; 008 import java.util.Collections; 009 import java.util.Date; 010 import java.util.HashMap; 011 import java.util.List; 012 import java.util.Locale; 013 import java.util.Map; 014 import java.util.logging.Level; 015 import java.util.logging.Logger; 016 017 import org.apache.wicket.Application; 018 import org.apache.wicket.model.IModel; 019 import org.hackystat.projectbrowser.ProjectBrowserApplication; 020 import org.hackystat.projectbrowser.ProjectBrowserSession; 021 import org.hackystat.projectbrowser.googlechart.ChartType; 022 import org.hackystat.projectbrowser.googlechart.GoogleChart; 023 import org.hackystat.projectbrowser.page.ProjectBrowserBasePage; 024 import org.hackystat.projectbrowser.page.loadingprocesspanel.Processable; 025 import org.hackystat.projectbrowser.page.trajectory.ProjectRecord; 026 import org.hackystat.projectbrowser.page.trajectory.dtw.DTWAlignment; 027 import org.hackystat.projectbrowser.page.trajectory.dtw.DTWException; 028 import org.hackystat.projectbrowser.page.trajectory.dtw.DTWFactory; 029 import org.hackystat.projectbrowser.page.trajectory.dtw.EuclideanDistance; 030 import org.hackystat.projectbrowser.page.trajectory.dtw.step.SymmetricStepFunction; 031 import org.hackystat.sensorbase.resource.projects.jaxb.Project; 032 import org.hackystat.telemetry.service.client.TelemetryClient; 033 import org.hackystat.telemetry.service.client.TelemetryClientException; 034 import org.hackystat.telemetry.service.resource.chart.jaxb.TelemetryPoint; 035 import org.hackystat.telemetry.service.resource.chart.jaxb.TelemetryStream; 036 import org.hackystat.utilities.logger.HackystatLogger; 037 import org.hackystat.utilities.tstamp.Tstamp; 038 039 /** 040 * Data model to hold state of the telemetry chart. 041 * 042 * @author Shaoxuan Zhang, Pavel Senin 043 */ 044 public class TrajectoryChartDataModel implements Serializable, Processable { 045 /** Support serialization. */ 046 private static final long serialVersionUID = 1L; 047 /** The granularity of the chart. Either Day, Week, or Month. */ 048 private String granularity = "Day"; 049 050 /** The project this user has selected. */ 051 private Map<Project, String> projectCharts = new HashMap<Project, String>(); 052 053 /** The projects this user has selected. */ 054 private ProjectRecord selectedProject1 = new ProjectRecord(); 055 // /** The start date this user has selected. */ 056 // private long project1StartDate = 0; 057 // /** The end date this user has selected. */ 058 // private long project1EndDate = 0; 059 /** The total length of the timeseries within the project1. */ 060 private int selectedProject1StreamLength; 061 062 /** The projects this user has selected. */ 063 private ProjectRecord selectedProject2 = new ProjectRecord(); 064 // /** The start date this user has selected. */ 065 // private long project2StartDate = 0; 066 // /** The end date this user has selected. */ 067 // private long project2EndDate = 0; 068 /** The total length of the timeseries within the project2. */ 069 private int selectedProject2StreamLength; 070 071 /** The analysis this user has selected. */ 072 private String telemetryName = null; 073 /** The parameters for this telemetry chart. */ 074 private List<String> parameters = new ArrayList<String>(); 075 /** Store the data retrieved from telemetry service. */ 076 private Map<Project, List<SelectableTrajectoryStream>> projectStreamData = 077 new HashMap<Project, List<SelectableTrajectoryStream>>(); 078 /** Chart with selected project streams. */ 079 private String selectedChart = null; 080 /** the width of this chart. */ 081 private int width = 800; 082 /** the height of this chart. */ 083 private int height = 300; 084 /** state of data loading process. */ 085 private volatile boolean inProcess = false; 086 /** result of data loading. */ 087 private volatile boolean complete = false; 088 /** message to display when data loading is in process. */ 089 private String processingMessage = ""; 090 /** host of the telemetry host. */ 091 private String telemetryHost; 092 /** email of the user. */ 093 private String email; 094 /** password of the user. */ 095 private String password; 096 private int maxStreamLength; 097 098 /** Chart with selected project streams. */ 099 private String normalizedTSChart = null; 100 101 /** Chart with selected project streams. */ 102 private String dtwChart = null; 103 104 private static final String CR = "\n"; 105 private static final String IDNT = " "; 106 private static final String IDNT2 = " "; 107 private static final String MARK = "[DEBUG] "; 108 109 private String dtwStatistics = "dtw Placeholder"; 110 private String trajectoryDataWarningMessage = ""; 111 112 /** 113 * Set all the parameters for the current chart. 114 * 115 * @param selectedProject1 The trajectory project1. 116 * @param selectedProject2 The trajectory project2. 117 * @param telemetryName The selected telemetry stream name. 118 * @param granularity The seleceted granularity. 119 * @param parameters Additional telemetry parameters. 120 * 121 */ 122 public void setModel(ProjectRecord selectedProject1, ProjectRecord selectedProject2, 123 String telemetryName, String granularity, List<IModel> parameters) { 124 125 // okay, going to get some updates here 126 this.inProcess = true; 127 this.complete = false; 128 129 // but log first what we got from that form 130 getLogger().log( 131 Level.FINER, 132 MARK + "TrajectoryChartModelParameters: setModel()" + CR + IDNT + "selectedProject1: " 133 + selectedProject1.getProject().getName() + CR + IDNT + IDNT2 + "startDate: " 134 + selectedProject1.getStartDate() + CR + IDNT + IDNT2 + "endDate: " 135 + selectedProject1.getEndDate() + CR + IDNT + IDNT2 + "indent: " 136 + selectedProject1.getIndent() + CR + IDNT + "selectedProject2: " 137 + selectedProject2.getProject().getName() + CR + IDNT + IDNT2 + "startDate: " 138 + selectedProject2.getStartDate() + CR + IDNT + IDNT2 + "endDate: " 139 + selectedProject2.getEndDate() + CR + IDNT + IDNT2 + "indent: " 140 + selectedProject2.getIndent() + CR + IDNT + "telemetry: " + telemetryName + CR + IDNT 141 + "granularity: " + granularity + CR + IDNT + "parameters: " + CR 142 + parametersToLog(parameters)); 143 144 // clear the process message and get sensorbase config from session 145 this.processingMessage = ""; 146 this.telemetryHost = ((ProjectBrowserApplication) ProjectBrowserSession.get().getApplication()) 147 .getTelemetryHost(); 148 this.email = ProjectBrowserSession.get().getEmail(); 149 this.password = ProjectBrowserSession.get().getPassword(); 150 151 // reset and set parameters 152 this.selectedProject1 = selectedProject1; 153 this.selectedProject2 = selectedProject2; 154 this.granularity = granularity; 155 this.telemetryName = telemetryName; 156 this.projectCharts.clear(); 157 this.projectStreamData.clear(); 158 this.parameters.clear(); 159 for (IModel model : parameters) { 160 if (model.getObject() != null) { 161 this.parameters.add(model.getObject().toString()); 162 } 163 } 164 this.selectedChart = null; 165 this.normalizedTSChart = null; 166 this.dtwChart = null; 167 } 168 169 /** 170 * Load data from Hackystat service. 171 */ 172 public void loadData() { 173 Logger logger = HackystatLogger.getLogger("org.hackystat.projectbrowser", "projectbrowser"); 174 this.inProcess = true; 175 this.complete = false; 176 logger.log(Level.FINER, MARK + "TrajectoryChartDataModel: loadData() INPROCESS: " 177 + isInProcess() + ", COMPLETE: " + isComplete()); 178 this.processingMessage = "Retrieving telemetry chart <" + getTelemetryName() 179 + "> from Hackystat Telemetry service.\n"; 180 try { 181 TelemetryClient client = new TelemetryClient(this.telemetryHost, this.email, this.password); 182 183 // Loading data for the project #1 184 // 185 List<SelectableTrajectoryStream> streamList = new ArrayList<SelectableTrajectoryStream>(); 186 Project project = selectedProject1.getProject(); 187 188 this.processingMessage += "Retrieving data for project: " + project.getName() + ".\n"; 189 logger.log(Level.FINER, MARK + "Retrieving chart <" + getTelemetryName() 190 + "> data from the telemetry: " + CR + IDNT + "project: " 191 + selectedProject1.getProject().getName() + CR + IDNT + "start: " 192 + Tstamp.makeTimestamp(selectedProject1.getStartDate().getTime()) + CR + IDNT + "end: " 193 + Tstamp.makeTimestamp(selectedProject1.getEndDate().getTime()) + CR + IDNT 194 + "parameters: " + this.getParameterAsString()); 195 196 List<TelemetryStream> streams = client.getChart(this.getTelemetryName(), project.getOwner(), 197 project.getName(), granularity, 198 Tstamp.makeTimestamp(selectedProject1.getStartDate().getTime()), 199 Tstamp.makeTimestamp(selectedProject1.getEndDate().getTime()), 200 this.getParameterAsString()).getTelemetryStream(); 201 202 for (TelemetryStream stream : streams) { 203 streamList.add(new SelectableTrajectoryStream(stream, selectedProject1.getIndent())); 204 } 205 this.projectStreamData.put(project, streamList); 206 207 if (streams.isEmpty()) { 208 selectedProject1StreamLength = -1; 209 } 210 else { 211 selectedProject1StreamLength = streams.get(0).getTelemetryPoint().size(); 212 selectedProject1.setStreamColor(GoogleChart.getNextJetColor()); 213 } 214 logger.log(Level.FINER, MARK + "Finished retrieving chart <" + getTelemetryName() 215 + "> for project: " + project.getName() + " streams retrieved: " + streams.size() 216 + " length: " + selectedProject1StreamLength); 217 218 // Loading data for the project #2 219 // 220 streamList = new ArrayList<SelectableTrajectoryStream>(); 221 project = selectedProject2.getProject(); 222 223 this.processingMessage += "Retrieving data for project: " + project.getName() + ".\n"; 224 logger.log(Level.FINER, MARK + "Retrieving chart <" + getTelemetryName() 225 + "> data from the telemetry: " + CR + IDNT + "project: " 226 + selectedProject2.getProject().getName() + CR + IDNT + "start: " 227 + Tstamp.makeTimestamp(selectedProject2.getStartDate().getTime()) + CR + IDNT + "end: " 228 + Tstamp.makeTimestamp(selectedProject2.getEndDate().getTime()) + CR + IDNT 229 + "parameters: " + this.getParameterAsString()); 230 231 streams = client.getChart(this.getTelemetryName(), project.getOwner(), project.getName(), 232 granularity, Tstamp.makeTimestamp(selectedProject2.getStartDate().getTime()), 233 Tstamp.makeTimestamp(selectedProject2.getEndDate().getTime()), 234 this.getParameterAsString()).getTelemetryStream(); 235 236 for (TelemetryStream stream : streams) { 237 streamList.add(new SelectableTrajectoryStream(stream)); 238 } 239 this.projectStreamData.put(project, streamList); 240 241 if (streams.isEmpty()) { 242 selectedProject2StreamLength = -1; 243 } 244 else { 245 selectedProject2StreamLength = streams.get(0).getTelemetryPoint().size(); 246 selectedProject2.setStreamColor(GoogleChart.getNextJetColor()); 247 } 248 logger.log(Level.FINER, MARK + "Finished retrieving chart <" + getTelemetryName() 249 + "> for project: " + project.getName() + " streams retrieved: " + streams.size() 250 + " length: " + selectedProject2StreamLength); 251 252 // save the maximal stream length 253 // 254 maxStreamLength = Math.max(selectedProject1StreamLength + this.selectedProject1.getIndent(), 255 selectedProject2StreamLength + this.selectedProject2.getIndent()); 256 logger.log(Level.FINER, MARK + "the longest stream is " + maxStreamLength + " points."); 257 258 if (selectedProject1StreamLength != selectedProject2StreamLength) { 259 this.trajectoryDataWarningMessage = "You've selected streams of different length, " 260 + "the open-endede DTW is not implemented yet."; 261 } 262 263 } 264 catch (TelemetryClientException e) { 265 String errorMessage = "Errors when retrieving " + getTelemetryName() + " telemetry data: " 266 + e.getMessage() + ". Please try again."; 267 this.processingMessage += errorMessage + "\n"; 268 269 logger.warning(errorMessage); 270 this.complete = false; 271 this.inProcess = false; 272 return; 273 } 274 275 this.inProcess = false; 276 this.complete = true; 277 this.processingMessage += "All done.\n"; 278 logger.log(Level.FINER, "[DEBUG] TrajectoryChartDataModel: loadData() INPROCESS: " 279 + isInProcess() + ", COMPLETE: " + isComplete()); 280 281 } 282 283 /** 284 * Cancel the data loading process. 285 */ 286 public void cancelDataLoading() { 287 this.processingMessage += "Process Cancelled.\n"; 288 this.inProcess = false; 289 } 290 291 /** 292 * Returns the start date in yyyy-MM-dd format. 293 * 294 * @return The date as a simple string. 295 */ 296 public String getProject1StartDateString() { 297 SimpleDateFormat format = new SimpleDateFormat(ProjectBrowserBasePage.DATA_FORMAT, 298 Locale.ENGLISH); 299 return format.format(new Date(this.selectedProject1.getStartDate().getTime())); 300 } 301 302 /** 303 * Returns the end date in yyyy-MM-dd format. 304 * 305 * @return The date as a simple string. 306 */ 307 public String getProject1EndDateString() { 308 SimpleDateFormat format = new SimpleDateFormat(ProjectBrowserBasePage.DATA_FORMAT, 309 Locale.ENGLISH); 310 return format.format(new Date(this.selectedProject1.getEndDate().getTime())); 311 } 312 313 /** 314 * Returns the start date in yyyy-MM-dd format. 315 * 316 * @return The date as a simple string. 317 */ 318 public String getProject2StartDateString() { 319 SimpleDateFormat format = new SimpleDateFormat(ProjectBrowserBasePage.DATA_FORMAT, 320 Locale.ENGLISH); 321 return format.format(new Date(this.selectedProject2.getStartDate().getTime())); 322 } 323 324 /** 325 * Returns the end date in yyyy-MM-dd format. 326 * 327 * @return The date as a simple string. 328 */ 329 public String getProject2EndDateString() { 330 SimpleDateFormat format = new SimpleDateFormat(ProjectBrowserBasePage.DATA_FORMAT, 331 Locale.ENGLISH); 332 return format.format(new Date(this.selectedProject2.getEndDate().getTime())); 333 } 334 335 /** 336 * @return the telemetryName 337 */ 338 public String getTelemetryName() { 339 return telemetryName; 340 } 341 342 /** 343 * Returns true if this model does not contain any data. 344 * 345 * @return True if no data. 346 */ 347 public boolean isEmpty() { 348 return (null == this.selectedProject1) && (null == this.selectedProject2); 349 } 350 351 /** 352 * Return the list of TelemetryStream associated with the given project. 353 * 354 * @param project the given project. 355 * @return the list of TelemetryStream. 356 */ 357 public List<SelectableTrajectoryStream> getTrajectoryStream(Project project) { 358 List<SelectableTrajectoryStream> streamList = this.projectStreamData.get(project); 359 if (streamList == null) { 360 streamList = new ArrayList<SelectableTrajectoryStream>(); 361 } 362 return streamList; 363 } 364 365 /** 366 * @return the selectedChart 367 */ 368 public String getSelectedChart() { 369 getLogger() 370 .log(Level.FINER, MARK + "TrajectoryChartDataModel: getting selected TelemetryChart"); 371 return selectedChart; 372 } 373 374 /** 375 * @return true if the chart is empty. 376 */ 377 public boolean isChartEmpty() { 378 return selectedChart == null || "".equals(selectedChart); 379 } 380 381 /** 382 * @return the selectedChart 383 */ 384 public String getNormalizedTSChart() { 385 getLogger().log(Level.FINER, MARK + "TrajectoryChartDataModel, getting Normalized TS chart"); 386 return normalizedTSChart; 387 } 388 389 /** 390 * @return true if the chart is empty. 391 */ 392 public boolean isNormalizedTSChartEmpty() { 393 return normalizedTSChart == null || "".equals(normalizedTSChart); 394 } 395 396 /** 397 * @return the selectedChart 398 */ 399 public String getDTWChart() { 400 getLogger().log(Level.FINER, MARK + "TrajectoryChartDataModel, getting DTW chart"); 401 return dtwChart; 402 } 403 404 /** 405 * @return true if the chart is empty. 406 */ 407 public boolean isDTWChartEmpty() { 408 return dtwChart == null || "".equals(dtwChart); 409 } 410 411 /** 412 * update the selectedChart. 413 * 414 * @return true if the chart is successfully updated. 415 */ 416 public boolean updateSelectedChart() { 417 getLogger().log(Level.FINER, MARK + "TelemetryChart: updating"); 418 // Since we have only two projects here let's plot them 419 // 420 List<SelectableTrajectoryStream> streams = new ArrayList<SelectableTrajectoryStream>(); 421 boolean dashed = false; 422 ArrayList<ProjectRecord> rec = new ArrayList<ProjectRecord>(); 423 rec.add(selectedProject1); 424 rec.add(selectedProject2); 425 for (ProjectRecord projectRec : rec) { 426 Project project = projectRec.getProject(); 427 getLogger() 428 .log(Level.FINER, MARK + "TelemetryChart: processing project " + project.getName()); 429 List<SelectableTrajectoryStream> streamList = this.getTrajectoryStream(project); 430 getLogger().log(Level.FINER, 431 MARK + "TelemetryChart: found " + streamList.size() + " stream(s)."); // NOPMD 432 String projectMarker = null; 433 double thickness = 2; 434 double lineLength = 1; 435 double blankLength = 0; 436 boolean isLineStylePicked = false; 437 for (SelectableTrajectoryStream stream : streamList) { 438 getLogger().log( 439 Level.FINER, 440 MARK + "TelemetryChart: processing stream " + stream.getTelemetryStream().getName() 441 + ", selected: " + stream.isSelected() + ", length: " 442 + stream.getStreamData().size()); 443 if (stream.isSelected()) { 444 if (projectMarker == null) { 445 projectMarker = GoogleChart.getNextMarker(); 446 } 447 if (!isLineStylePicked) { 448 isLineStylePicked = true; 449 if (dashed) { 450 lineLength = 6; 451 blankLength = 3; 452 } 453 dashed = !dashed; 454 } 455 stream.setMarker(projectMarker); 456 stream.setThickness(thickness); 457 stream.setLineLength(lineLength); 458 stream.setBlankLength(blankLength); 459 stream.setIndent(projectRec.getIndent()); 460 streams.add(stream); 461 stream.setColor(projectRec.getStreamColor()); 462 } 463 else { 464 stream.setColor(""); 465 stream.setMarker(""); 466 } 467 } 468 } 469 if (streams.isEmpty()) { 470 selectedChart = ""; 471 return false; 472 } 473 selectedChart = this.getTelemetryChartURL(streams); 474 getLogger().log(Level.FINER, MARK + "TelemetryChart URL: " + selectedChart); 475 return true; 476 } 477 478 /** 479 * Extract the time series from the selectedChart, normalize those and chart them. 480 * 481 * @return true if the chart is successfully updated. 482 */ 483 public boolean updateNormalizedTSChart() { 484 getLogger().log(Level.FINER, MARK + "Normalized TS Chart: updating."); 485 boolean dashed = false; 486 // Since we have only two projects here let's plot them 487 // 488 // create the list of streams we are about to plot 489 List<SelectableTrajectoryStream> streams = new ArrayList<SelectableTrajectoryStream>(); 490 // 491 // make sure we do know which projects we are working on 492 ArrayList<ProjectRecord> rec = new ArrayList<ProjectRecord>(); 493 rec.add(selectedProject1); 494 rec.add(selectedProject2); 495 // 496 // now, iterate over this projects and make chart happen 497 for (ProjectRecord projectRec : rec) { 498 Project project = projectRec.getProject(); 499 getLogger().log(Level.FINER, 500 MARK + "Normalized TS Chart: processing project " + project.getName()); 501 List<SelectableTrajectoryStream> streamList = this.getTrajectoryStream(project); 502 getLogger().log(Level.FINER, 503 MARK + "Normalized TS Chart: found " + streamList.size() + " stream(s)."); 504 String projectMarker = null; 505 double thickness = 2; 506 double lineLength = 1; 507 double blankLength = 0; 508 boolean isLineStylePicked = false; 509 for (SelectableTrajectoryStream stream : streamList) { 510 getLogger().log( 511 Level.FINER, 512 MARK + "Normalized TS Chart: processing stream " 513 + stream.getTelemetryStream().getName() + ", selected: " + stream.isSelected() 514 + ", length: " + stream.getStreamData().size()); 515 if (stream.isSelected()) { 516 if (projectMarker == null) { 517 projectMarker = GoogleChart.getNextMarker(); 518 } 519 if (!isLineStylePicked) { 520 isLineStylePicked = true; 521 if (dashed) { 522 lineLength = 6; 523 blankLength = 3; 524 } 525 dashed = !dashed; 526 } 527 stream.setMarker(projectMarker); 528 stream.setThickness(thickness); 529 stream.setLineLength(lineLength); 530 stream.setBlankLength(blankLength); 531 stream.setIndent(projectRec.getIndent()); 532 streams.add(stream); 533 } 534 else { 535 stream.setColor(""); 536 stream.setMarker(""); 537 } 538 } 539 } 540 if (streams.isEmpty()) { 541 normalizedTSChart = ""; 542 return false; 543 } 544 normalizedTSChart = this.getNormalizedTSChartURL(streams); 545 getLogger().log(Level.FINER, MARK + "Normalized TS Chart URL: " + normalizedTSChart); 546 return true; 547 } 548 549 /** 550 * Extract the time series from the selectedChart, normalize those and chart them. 551 * 552 * @return true if the chart is successfully updated. 553 */ 554 public boolean updateDTWChart() { 555 getLogger().log(Level.FINER, MARK + "DTW Chart: updating."); 556 boolean dashed = false; 557 // Since we have only two projects here let's plot them 558 // 559 // create the list of streams we are about to plot 560 List<SelectableTrajectoryStream> streams = new ArrayList<SelectableTrajectoryStream>(); 561 // 562 // make sure we do know which projects we are working on 563 ArrayList<ProjectRecord> rec = new ArrayList<ProjectRecord>(); 564 rec.add(selectedProject1); 565 rec.add(selectedProject2); 566 // 567 // now, iterate over this projects and make chart happen 568 for (ProjectRecord projectRec : rec) { 569 Project project = projectRec.getProject(); 570 getLogger().log(Level.FINER, MARK + "DTW Chart: processing project " + project.getName()); 571 List<SelectableTrajectoryStream> streamList = this.getTrajectoryStream(project); 572 getLogger().log(Level.FINER, MARK + "DTW Chart: found " + streamList.size() + " stream(s)."); 573 String projectMarker = null; 574 double thickness = 2; 575 double lineLength = 1; 576 double blankLength = 0; 577 boolean isLineStylePicked = false; 578 for (SelectableTrajectoryStream stream : streamList) { 579 getLogger().log( 580 Level.FINER, 581 MARK + "DTW Chart: processing stream " + stream.getTelemetryStream().getName() 582 + ", selected: " + stream.isSelected() + ", length: " 583 + stream.getStreamData().size()); 584 if (stream.isSelected()) { 585 if (projectMarker == null) { 586 projectMarker = GoogleChart.getNextMarker(); 587 } 588 if (!isLineStylePicked) { 589 isLineStylePicked = true; 590 if (dashed) { 591 lineLength = 6; 592 blankLength = 3; 593 } 594 dashed = !dashed; 595 } 596 stream.setMarker(projectMarker); 597 stream.setThickness(thickness); 598 stream.setLineLength(lineLength); 599 stream.setBlankLength(blankLength); 600 stream.setIndent(projectRec.getIndent()); 601 streams.add(stream); 602 } 603 else { 604 stream.setColor(""); 605 stream.setMarker(""); 606 } 607 } 608 } 609 if (streams.isEmpty()) { 610 dtwChart = ""; 611 return false; 612 } 613 dtwChart = this.getDTWChartURL(streams); 614 getLogger().log(Level.FINER, MARK + "DTW chart URL:" + dtwChart); 615 return true; 616 } 617 618 /** 619 * Get the DTW statistics. 620 * 621 * @return DTW statistics. 622 */ 623 public String getDTWStatistics() { 624 return this.dtwStatistics; 625 } 626 627 /** 628 * Return the google chart url that present all streams within the given list. 629 * 630 * @param streams the given stream list. 631 * @return the URL string of the chart. 632 */ 633 public String getTelemetryChartURL(List<SelectableTrajectoryStream> streams) { 634 getLogger().log(Level.FINER, 635 MARK + "TelemetryChart: getting url for " + streams.size() + " stream(s)."); 636 GoogleChart googleChart = new GoogleChart(ChartType.LINE, this.width, this.height); 637 TrajectoryStreamYAxis streamAxis = null; 638 639 // make up the Y axis for all streams 640 for (SelectableTrajectoryStream stream : streams) { 641 if (stream.isEmpty()) { 642 continue; 643 } 644 getLogger().log( 645 Level.FINER, 646 MARK + "TelemetryChart: processing stream " + stream.getTelemetryStream().getName() 647 + ", length " + stream.getTelemetryStream().getTelemetryPoint().size()); 648 double streamMax = stream.getMaximum(); 649 double streamMin = stream.getMinimum(); 650 String streamUnitName = stream.getUnitName(); 651 if (streamAxis == null) { 652 streamAxis = newYAxis(streamUnitName, streamMax, streamMin); 653 } 654 else { 655 double axisMax = streamAxis.getMaximum(); 656 double axisMin = streamAxis.getMinimum(); 657 streamAxis.setMaximum((axisMax > streamMax) ? axisMax : streamMax); 658 streamAxis.setMinimum((axisMin < streamMin) ? axisMin : streamMin); 659 } 660 // stream.setColor(GoogleChart.getNextJetColor()); 661 } 662 // add the y axis and extend the range if it contains only one horizontal line. 663 String axisType = "r"; 664 if (googleChart.isYAxisEmpty()) { 665 axisType = "y"; 666 } 667 List<Double> rangeList = new ArrayList<Double>(); 668 // extend the range of the axis if it contains only one vertical straight line. 669 if (streamAxis.getMaximum() - streamAxis.getMinimum() < 0.1) { 670 streamAxis.setMaximum(streamAxis.getMaximum() + 1.0); 671 streamAxis.setMinimum(streamAxis.getMinimum() - 1.0); 672 } 673 rangeList.add(streamAxis.getMinimum()); 674 rangeList.add(streamAxis.getMaximum()); 675 // add the axis to the chart. 676 googleChart.addAxisRange(axisType, rangeList, streamAxis.getColor()); 677 678 // add streams to the chart. 679 for (int i = 0; i < streams.size(); ++i) { 680 SelectableTrajectoryStream stream = streams.get(i); 681 if (!stream.isEmpty()) { 682 this.addStreamToChart(stream, streamAxis.getMinimum(), streamAxis.getMaximum(), 683 maxStreamLength, googleChart); 684 } 685 } 686 // add the x axis, X axis is a list of markers 687 if (!streams.isEmpty()) { 688 googleChart.addAxisLabel("x", getMarkersList(), ""); 689 } 690 return googleChart.getUrl(); 691 } 692 693 /** 694 * Return the google chart url that present normalized streams. 695 * 696 * @param streams the given stream list. 697 * @return the URL string of the chart. 698 */ 699 public String getNormalizedTSChartURL(List<SelectableTrajectoryStream> streams) { 700 getLogger().log(Level.FINER, 701 MARK + "Normalized TS: getting url for " + streams.size() + " stream(s)."); 702 GoogleChart googleChart = new GoogleChart(ChartType.LINE, this.width, this.height); 703 TrajectoryStreamYAxis streamAxis = null; 704 705 // okay here we are expecting to see two streams in the input array - get them normalized. 706 double streamMin = Double.MAX_VALUE; 707 double streamMax = Double.MIN_VALUE; 708 for (SelectableTrajectoryStream stream : streams) { 709 if (!stream.isEmpty()) { 710 List<Double> normalizedStreamData = stream.getNormalizedStreamData(); 711 if (streamMin > Collections.min(normalizedStreamData)) { 712 streamMin = Collections.min(normalizedStreamData); 713 } 714 if (streamMax < Collections.max(normalizedStreamData)) { 715 streamMax = Collections.max(normalizedStreamData); 716 } 717 } 718 } 719 getLogger().log(Level.FINER, 720 MARK + "Normalized TS: min: " + streamMin + ", maximum: " + streamMax); 721 722 List<List<Double>> normalizedStreams = new ArrayList<List<Double>>(); 723 724 // make up the Y axis for all streams 725 for (SelectableTrajectoryStream stream : streams) { 726 if (!stream.isEmpty()) { 727 String streamUnitName = stream.getUnitName(); 728 streamAxis = newYAxis(streamUnitName, streamMax, streamMin); 729 //stream.setColor(GoogleChart.getNextJetColor()); 730 } 731 } 732 733 // add the y axis and extend the range if it contains only one horizontal line. 734 String axisType = "r"; 735 if (googleChart.isYAxisEmpty()) { 736 axisType = "y"; 737 } 738 739 List<Double> rangeList = new ArrayList<Double>(); 740 rangeList.add(streamAxis.getMinimum()); 741 rangeList.add(streamAxis.getMaximum()); 742 743 // add the axis to the chart. 744 googleChart.addAxisRange(axisType, rangeList, streamAxis.getColor()); 745 746 // add streams to the chart. 747 for (int i = 0; i < streams.size(); ++i) { 748 SelectableTrajectoryStream stream = streams.get(i); 749 if (!stream.isEmpty()) { 750 List<Double> s = this.addStreamToNormalizedChart(stream, streamAxis.getMinimum(), 751 streamAxis.getMaximum(), maxStreamLength, googleChart); 752 normalizedStreams.add(s); 753 } 754 } 755 // add the x axis, X axis is a list of markers 756 if (!streams.isEmpty()) { 757 googleChart.addAxisLabel("x", getMarkersList(), ""); 758 try { 759 NumberFormat decFormatter = new DecimalFormat("00.00"); 760 googleChart.setTitle("Normalized streamData|Euclidean distance=" 761 + decFormatter.format(this.getEuclideanDistance(normalizedStreams.get(0), 762 normalizedStreams.get(1)))); 763 } 764 catch (DTWException e) { 765 e.printStackTrace(); 766 } 767 } 768 return googleChart.getUrl(); 769 } 770 771 /** 772 * Calculates the Euclidean distance. 773 * 774 * @param s1 time series 1. 775 * @param s2 time series 2. 776 * @return the Euclidean distance. 777 * @throws DTWException if error occures. 778 */ 779 private Double getEuclideanDistance(List<Double> s1, List<Double> s2) throws DTWException { 780 double[][] stream1 = new double[s1.size()][2]; 781 int i = 0; 782 for (Double d : s1) { 783 stream1[i][0] = Integer.valueOf(i).doubleValue(); 784 stream1[i][1] = d.doubleValue(); 785 i++; 786 } 787 double[][] stream2 = new double[s2.size()][2]; 788 i = 0; 789 for (Double d : s2) { 790 stream2[i][0] = Integer.valueOf(i).doubleValue(); 791 stream2[i][1] = d.doubleValue(); 792 i++; 793 } 794 return EuclideanDistance.getSeriesDistnace(stream1, stream2); 795 } 796 797 /** 798 * Return the google chart url that present normalized streams. 799 * 800 * @param streams the given stream list. 801 * @return the URL string of the chart. 802 */ 803 public String getDTWChartURL(List<SelectableTrajectoryStream> streams) { 804 getLogger().log(Level.FINER, 805 MARK + "DTW Chart: getting url for " + streams.size() + " stream(s)."); 806 GoogleChart googleChart = new GoogleChart(ChartType.LINE, this.width, this.height); 807 TrajectoryStreamYAxis streamAxis = null; 808 809 // okay here we are expecting to see two streams in the input array - get them normalized. 810 double streamMin = Double.MAX_VALUE; 811 double streamMax = Double.MIN_VALUE; 812 for (SelectableTrajectoryStream stream : streams) { 813 if (!stream.isEmpty()) { 814 List<Double> normalizedStreamData = stream.getNormalizedStreamData(); 815 if (streamMin > Collections.min(normalizedStreamData)) { 816 streamMin = Collections.min(normalizedStreamData); 817 } 818 if (streamMax < Collections.max(normalizedStreamData)) { 819 streamMax = Collections.max(normalizedStreamData); 820 } 821 } 822 } 823 824 getLogger().log(Level.FINER, 825 MARK + "Normalized TS: min: " + streamMin + ", maximum: " + streamMax); 826 827 // make up the Y axis for all streams 828 for (SelectableTrajectoryStream stream : streams) { 829 if (stream.isEmpty()) { 830 continue; 831 } 832 getLogger().log( 833 Level.FINER, 834 MARK + "DTW Chart: processing stream " + stream.getTelemetryStream().getName() 835 + ", length " + stream.getTelemetryStream().getTelemetryPoint().size()); 836 String streamUnitName = stream.getUnitName(); 837 streamAxis = newYAxis(streamUnitName, streamMax, streamMin); 838 //stream.setColor(GoogleChart.getNextJetColor()); 839 } 840 841 // add the y axis and extend the range if it contains only one horizontal line. 842 String axisType = "r"; 843 if (googleChart.isYAxisEmpty()) { 844 axisType = "y"; 845 } 846 847 List<Double> rangeList = new ArrayList<Double>(); 848 rangeList.add(streamAxis.getMinimum()); 849 rangeList.add(streamAxis.getMaximum()); 850 851 // add the axis to the chart. 852 googleChart.addAxisRange(axisType, rangeList, streamAxis.getColor()); 853 854 // run DTW in here 855 // 856 // extract time series as the list of double values 857 List<List<Double>> rawSeries = this.getRawSeriesForDTW(streams); 858 List<List<Double>> dtwSeries = this.getDTWSeries(rawSeries); 859 860 // add streams to the chart. 861 // add streams to the chart. 862 for (int i = 0; i < streams.size(); ++i) { 863 SelectableTrajectoryStream stream = streams.get(i); 864 if (!stream.isEmpty()) { 865 this.addStreamToDTWChart(stream, dtwSeries.get(i), streamAxis.getMinimum(), streamAxis 866 .getMaximum(), maxStreamLength, googleChart); 867 } 868 } 869 870 // add the x axis, X axis is a list of markers 871 if (!streams.isEmpty()) { 872 googleChart.addAxisLabel("x", getMarkersList(), ""); 873 try { 874 NumberFormat decFormatter = new DecimalFormat("00.00"); 875 googleChart.setTitle("Normalized streamData|Euclidean distance=" 876 + decFormatter.format(this.getEuclideanDistance(dtwSeries.get(0), dtwSeries.get(1)))); 877 } 878 catch (DTWException e) { 879 e.printStackTrace(); 880 } 881 } 882 return googleChart.getUrl(); 883 } 884 885 /** 886 * Performs DTW alignment. 887 * 888 * @param rawSeries the series to align. 889 * @return aligned series. 890 */ 891 private List<List<Double>> getDTWSeries(List<List<Double>> rawSeries) { 892 893 List<List<Double>> res = new ArrayList<List<Double>>(); 894 895 List<Double> query = rawSeries.get(0); 896 double[][] queryD = new double[query.size()][1]; 897 int i = 0; 898 for (Double n : query) { 899 queryD[i][0] = n.doubleValue(); 900 i++; 901 } 902 903 List<Double> template = rawSeries.get(1); 904 double[][] templateD = new double[template.size()][1]; 905 i = 0; 906 for (Double n : template) { 907 templateD[i][0] = n.doubleValue(); 908 i++; 909 } 910 911 try { 912 DTWAlignment r = DTWFactory.doDTW(queryD, templateD, SymmetricStepFunction.STEP_PATTERN_P0); 913 // getLogger().log(Level.FINER, MARK + "DTW Record: " + CR + r.toString()); 914 915 double[] rawWarpingQuery = r.getWarpingQuery(); 916 List<Double> warpingQuery = new ArrayList<Double>(); 917 for (int k = 0; k < rawWarpingQuery.length; k++) { 918 warpingQuery.add(Double.valueOf(rawWarpingQuery[k])); 919 } 920 921 res.add(warpingQuery); 922 res.add(template); 923 return res; 924 } 925 catch (DTWException e) { 926 // TODO Auto-generated catch block 927 e.printStackTrace(); 928 } 929 return null; 930 } 931 932 /** 933 * Prepares series for the alignment - extracts data from streams. 934 * 935 * @param streams input streams 936 * @return extracted data. 937 */ 938 private List<List<Double>> getRawSeriesForDTW(List<SelectableTrajectoryStream> streams) { 939 getLogger().log(Level.FINER, MARK + "DTW Chart: getting raw series."); 940 List<List<Double>> res = new ArrayList<List<Double>>(); 941 // add streams to the chart. 942 for (int i = 0; i < streams.size(); ++i) { 943 SelectableTrajectoryStream stream = streams.get(i); 944 if (!stream.isEmpty()) { 945 res.add(stream.getNormalizedStreamData()); 946 } 947 } 948 if (!res.isEmpty()) { 949 StringBuffer sb = new StringBuffer(1000); 950 NumberFormat decFormatter = new DecimalFormat("####0.00"); 951 for (List<Double> l : res) { 952 for (Double d : l) { 953 sb.append(decFormatter.format(d)); 954 sb.append(", "); 955 } 956 sb.append('|'); 957 } 958 getLogger().log(Level.FINER, MARK + "DTW Chart: RAW series: " + sb.toString()); 959 return res; 960 } 961 return null; 962 } 963 964 /** 965 * Add the given stream to the google chart. And return the maximum value of the stream. 966 * 967 * @param stream the given stream. 968 * @param maximum the maximum value of the range this stream will be associated to. 969 * @param minimum the minimum value of the range this stream will be associated to. 970 * @param maxStreamLength the plotted stream length on X axis. 971 * @param googleChart the google chart. 972 */ 973 public void addStreamToChart(SelectableTrajectoryStream stream, double minimum, double maximum, 974 Integer maxStreamLength, GoogleChart googleChart) { 975 // add the chart only if the stream is not empty. 976 if (!stream.isEmpty()) { 977 // add stream data 978 googleChart.getChartData().add(stream.getStreamData(maxStreamLength)); 979 // prepare the range data. 980 List<Double> range = getRangeList(minimum, maximum); 981 // add range 982 googleChart.getChartDataScale().add(range); 983 // add stream color 984 googleChart.addColor(stream.getColor()); 985 // add line style; 986 googleChart.addLineStyle(stream.getThickness(), stream.getLineLength(), stream 987 .getBlankLength()); 988 /* googleChart.addAxisRange(axisType, range, randomColor); */ 989 // add markers 990 /* stream.setMarker(GoogleChart.getRandomMarker()); */ 991 googleChart.getChartMarker().add(stream.getMarker()); 992 googleChart.getMarkerColors().add(stream.getColor()); 993 } 994 } 995 996 /** 997 * Add the given stream to the google chart and return the normalized data. 998 * 999 * @param stream the given stream. 1000 * @param maximum the maximum value of the range this stream will be associated to. 1001 * @param minimum the minimum value of the range this stream will be associated to. 1002 * @param maxStreamLength the plotted stream length on X axis. 1003 * @param googleChart the google chart. 1004 * 1005 * @return normalized data extracted from the stream. 1006 */ 1007 public List<Double> addStreamToNormalizedChart(SelectableTrajectoryStream stream, 1008 double minimum, double maximum, Integer maxStreamLength, GoogleChart googleChart) { 1009 // add the chart only if the stream is not empty. 1010 if (!stream.isEmpty()) { 1011 // add stream data 1012 List<Double> res = stream.getNormalizedStreamData(); 1013 getLogger().log(Level.FINER, "TrajectoryChart: adding series: " + res); 1014 googleChart.getChartData().add(res); 1015 // prepare the range data. 1016 List<Double> range = getRangeList(minimum, maximum); 1017 // add range 1018 googleChart.getChartDataScale().add(range); 1019 // add stream color 1020 googleChart.addColor(stream.getColor()); 1021 // add line style; 1022 googleChart.addLineStyle(stream.getThickness(), stream.getLineLength(), stream 1023 .getBlankLength()); 1024 /* googleChart.addAxisRange(axisType, range, randomColor); */ 1025 // add markers 1026 /* stream.setMarker(GoogleChart.getRandomMarker()); */ 1027 googleChart.getChartMarker().add(stream.getMarker()); 1028 googleChart.getMarkerColors().add(stream.getColor()); 1029 return res; 1030 } 1031 return new ArrayList<Double>(); 1032 } 1033 1034 /** 1035 * Add the given stream to the google chart. And return the maximum value of the stream. 1036 * 1037 * @param stream the given stream. 1038 * @param list the chart data. 1039 * @param maximum the maximum value of the range this stream will be associated to. 1040 * @param minimum the minimum value of the range this stream will be associated to. 1041 * @param maxStreamLength the plotted stream length on X axis. 1042 * @param googleChart the google chart. 1043 */ 1044 public void addStreamToDTWChart(SelectableTrajectoryStream stream, List<Double> list, 1045 double minimum, double maximum, Integer maxStreamLength, GoogleChart googleChart) { 1046 // add the chart only if the stream is not empty. 1047 if (!stream.isEmpty()) { 1048 // add stream data 1049 googleChart.getChartData().add(list); 1050 // prepare the range data. 1051 List<Double> range = getRangeList(minimum, maximum); 1052 // add range 1053 googleChart.getChartDataScale().add(range); 1054 // add stream color 1055 googleChart.addColor(stream.getColor()); 1056 // add line style; 1057 googleChart.addLineStyle(stream.getThickness(), stream.getLineLength(), stream 1058 .getBlankLength()); 1059 /* googleChart.addAxisRange(axisType, range, randomColor); */ 1060 // add markers 1061 /* stream.setMarker(GoogleChart.getRandomMarker()); */ 1062 googleChart.getChartMarker().add(stream.getMarker()); 1063 googleChart.getMarkerColors().add(stream.getColor()); 1064 } 1065 } 1066 1067 /** 1068 * create a new TelemetryStreamYAxis instance with given parameters. 1069 * 1070 * @param streamUnitName the unit name 1071 * @param streamMax the maximum value 1072 * @param streamMin the minimum value 1073 * @return the new object 1074 */ 1075 private TrajectoryStreamYAxis newYAxis(String streamUnitName, double streamMax, 1076 double streamMin) { 1077 TrajectoryStreamYAxis axis = new TrajectoryStreamYAxis(streamUnitName); 1078 axis.setMaximum(streamMax); 1079 axis.setMinimum(streamMin); 1080 axis.setColor("00007F"); 1081 return axis; 1082 } 1083 1084 /** 1085 * return a list that contains the range minimum and maximum. The minimum value and maximum value 1086 * will be normalized accordingly. 1087 * 1088 * @param min the minimum value 1089 * @param max the maximum value 1090 * @return the list 1091 */ 1092 private List<Double> getRangeList(double min, double max) { 1093 double minimum = min; 1094 double maximum = max; 1095 List<Double> rangeList = new ArrayList<Double>(); 1096 // if (minimum == maximum) { 1097 // minimum -= 0.5; 1098 // minimum = (minimum < 0) ? 0 : minimum; 1099 // maximum += 0.5; 1100 // } 1101 // minimum = Math.floor(minimum); 1102 // maximum = Math.ceil(maximum); 1103 rangeList.add(minimum); 1104 rangeList.add(maximum); 1105 return rangeList; 1106 } 1107 1108 /** 1109 * Return the date list within the list of points. 1110 * 1111 * @param points the point list. 1112 * @return the date list. 1113 */ 1114 public List<String> getDateList(List<TelemetryPoint> points) { 1115 List<String> dates = new ArrayList<String>(); 1116 SimpleDateFormat dateFormat = new SimpleDateFormat("MM-dd", Locale.US); 1117 for (TelemetryPoint point : points) { 1118 dates.add(dateFormat.format(point.getTime().toGregorianCalendar().getTime())); 1119 } 1120 return dates; 1121 } 1122 1123 /** 1124 * Return the date list inside this model. 1125 * 1126 * @return the date list. 1127 */ 1128 public List<String> getMarkersList() { 1129 // check if both streams are empty 1130 if (isEmpty()) { 1131 return new ArrayList<String>(); 1132 } 1133 List<String> markersList = new ArrayList<String>(); 1134 for (int i = 0; i < maxStreamLength; i++) { 1135 markersList.add(String.valueOf(i)); 1136 } 1137 return markersList; 1138 } 1139 1140 /** 1141 * @return the selectedProjects 1142 */ 1143 public List<ProjectRecord> getSelectedProjects() { 1144 List<ProjectRecord> list = new ArrayList<ProjectRecord>(); 1145 list.add(selectedProject1); 1146 list.add(selectedProject2); 1147 return list; 1148 } 1149 1150 /** 1151 * Return the chart for the given project 1152 * 1153 * @param project the given project. 1154 * @return the chart link. 1155 */ 1156 /* 1157 * public String getProjectChart(Project project) { String chartLink = 1158 * this.projectCharts.get(project); if (chartLink == null) { chartLink = 1159 * this.getChartUrl(project); this.projectCharts.put(project, chartLink); } return chartLink; } 1160 */ 1161 1162 /** 1163 * Return the comma-separated list of parameters in String. 1164 * 1165 * @return the parameters as String 1166 */ 1167 public String getParameterAsString() { 1168 StringBuffer stringBuffer = new StringBuffer(); 1169 for (String model : this.parameters) { 1170 stringBuffer.append(model); 1171 stringBuffer.append(','); 1172 } 1173 String param = stringBuffer.toString(); 1174 if (param.length() >= 1) { 1175 param = param.substring(0, param.length() - 1); 1176 } 1177 return param; 1178 } 1179 1180 /** 1181 * @return the overallChart 1182 */ 1183 /* 1184 * public String getOverallChart() { if (overallChart == null) { List<TelemetryStream> streams = 1185 * new ArrayList<TelemetryStream>(); List<String> projectNames = new ArrayList<String>(); for 1186 * (Project project : this.selectedProjects) { List<SelectableTelemetryStream> streamList = 1187 * this.getTelemetryStream(project); for (int i = 0; i < streamList.size(); ++i) { 1188 * streams.add(streamList.get(i).getTelemetryStream()); projectNames.add(project.getName()); } } 1189 * overallChart = this.getChartUrl(projectNames, streams); } return overallChart; } 1190 */ 1191 /** 1192 * @param width the width to set 1193 */ 1194 public void setWidth(int width) { 1195 this.width = width; 1196 } 1197 1198 /** 1199 * @return the width 1200 */ 1201 public int getWidth() { 1202 return width; 1203 } 1204 1205 /** 1206 * @param height the height to set 1207 */ 1208 public void setHeight(int height) { 1209 this.height = height; 1210 } 1211 1212 /** 1213 * @return the height 1214 */ 1215 public int getHeight() { 1216 return height; 1217 } 1218 1219 /** 1220 * Return statistics for the stream1. 1221 * 1222 * @return the statistics for the stream1. 1223 */ 1224 public String getStream1Statistics() { 1225 return this.selectedProject1.toLabelMessage(); 1226 } 1227 1228 /** 1229 * Return statistics for the stream2. 1230 * 1231 * @return the statistics for the stream2. 1232 */ 1233 public String getStream2Statistics() { 1234 return this.selectedProject2.toLabelMessage(); 1235 } 1236 1237 /** 1238 * Change all selected flags of streams to the given flag. 1239 * 1240 * @param flag the boolean flag. 1241 */ 1242 public void changeSelectionForAll(boolean flag) { 1243 for (List<SelectableTrajectoryStream> streamList : this.projectStreamData.values()) { 1244 for (SelectableTrajectoryStream stream : streamList) { 1245 stream.setSelected(flag); 1246 } 1247 } 1248 } 1249 1250 /** 1251 * @return the inProcess 1252 */ 1253 public boolean isInProcess() { 1254 return inProcess; 1255 } 1256 1257 /** 1258 * @return the isComplete 1259 */ 1260 public boolean isComplete() { 1261 return complete; 1262 } 1263 1264 /** 1265 * @return the message 1266 */ 1267 public String getProcessingMessage() { 1268 return processingMessage; 1269 } 1270 1271 /** 1272 * @return the logger that associated to this web application. 1273 */ 1274 private Logger getLogger() { 1275 return ((ProjectBrowserApplication) Application.get()).getLogger(); 1276 } 1277 1278 /** 1279 * Helps to format parameters list. 1280 * 1281 * @param parameters The parameters list. 1282 * @return formatted parameters log message. 1283 */ 1284 private String parametersToLog(List<IModel> parameters) { 1285 StringBuffer sb = new StringBuffer(500); 1286 for (IModel im : parameters) { 1287 sb.append(IDNT + IDNT2 + im.getClass() + ": " + im.getObject() + CR); 1288 } 1289 return sb.toString(); 1290 } 1291 1292 /** 1293 * Set the paramErrorMessage. 1294 * 1295 * @param message the message. 1296 */ 1297 public void setWarningMessage(String message) { 1298 this.trajectoryDataWarningMessage = message; 1299 } 1300 1301 /** 1302 * @return the paramErrorMessage 1303 */ 1304 public String getWarningMessage() { 1305 String temp = this.trajectoryDataWarningMessage; 1306 this.trajectoryDataWarningMessage = ""; 1307 return temp; 1308 } 1309 1310 1311 }