Autopsy  4.13.0
Graphical digital forensics platform for The Sleuth Kit and other tools.
KMLReport.java
Go to the documentation of this file.
1 /*
2  *
3  * Autopsy Forensic Browser
4  *
5  * Copyright 2014-2018 Basis Technology Corp.
6  * contact: carrier <at> sleuthkit <dot> org
7  *
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  * http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  */
20 package org.sleuthkit.autopsy.report.modules.kml;
21 
23 import javax.swing.JPanel;
24 import org.openide.util.NbBundle;
28 import java.io.File;
29 import java.io.FileOutputStream;
30 import java.io.IOException;
31 import java.io.InputStream;
32 import java.io.OutputStream;
33 import java.nio.file.Path;
34 import java.nio.file.Paths;
35 import java.text.SimpleDateFormat;
36 import java.util.ArrayList;
37 import java.util.List;
38 import java.util.logging.Level;
39 import org.jdom2.Document;
40 import org.jdom2.Element;
41 import org.jdom2.Namespace;
42 import org.jdom2.output.Format;
43 import org.jdom2.output.XMLOutputter;
44 import org.jdom2.CDATA;
45 import org.openide.filesystems.FileUtil;
46 import org.openide.util.NbBundle.Messages;
54 import org.sleuthkit.datamodel.AbstractFile;
55 import org.sleuthkit.datamodel.ReadContentInputStream;
56 import org.sleuthkit.datamodel.ReadContentInputStream.ReadContentInputStreamException;
57 import org.sleuthkit.datamodel.SleuthkitCase;
58 import org.sleuthkit.datamodel.TskCoreException;
59 
63 public final class KMLReport implements GeneralReportModule {
64 
65  private static final Logger logger = Logger.getLogger(KMLReport.class.getName());
66  private static final String KML_STYLE_FILE = "style.kml";
67  private static final String REPORT_KML = "ReportKML.kml";
68  private static final String STYLESHEETS_PATH = "/org/sleuthkit/autopsy/report/stylesheets/";
69  private static KMLReport instance = null;
70  private Case currentCase;
71  private SleuthkitCase skCase;
72  private final SimpleDateFormat kmlDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX");
73  private Namespace ns;
74  private final static String HTML_PROP_FORMAT = "<b>%s: </b>%s<br>";
75 
76  private Element gpsExifMetadataFolder;
77  private Element gpsBookmarksFolder;
78  private Element gpsLastKnownLocationFolder;
79  private Element gpsRouteFolder;
80  private Element gpsSearchesFolder;
81  private Element gpsTrackpointsFolder;
82 
83  private List<Waypoint> waypointList = null;
84 
85  private enum FeatureColor {
86  RED("style.kml#redFeature"),
87  GREEN("style.kml#greenFeature"),
88  BLUE("style.kml#blueFeature"),
89  PURPLE("style.kml#purpleFeature"),
90  WHITE("style.kml#whiteFeature"),
91  YELLOW("style.kml#yellowFeature");
92  private final String color;
93 
94  FeatureColor(String color) {
95  this.color = color;
96  }
97 
98  String getColor() {
99  return this.color;
100  }
101  }
102 
103  // Hidden constructor for the report
104  private KMLReport() {
105  }
106 
107  // Get the default implementation of this report
108  public static synchronized KMLReport getDefault() {
109  if (instance == null) {
110  instance = new KMLReport();
111  }
112  return instance;
113  }
114 
122  @Messages({
123  "KMLReport.unableToExtractPhotos=Could not extract photo information.",
124  "KMLReport.exifPhotoError=Could not extract photos with EXIF metadata.",
125  "KMLReport.bookmarkError=Could not extract Bookmark information.",
126  "KMLReport.gpsBookmarkError=Could not get GPS Bookmarks from database.",
127  "KMLReport.locationError=Could not extract Last Known Location information.",
128  "KMLReport.locationDatabaseError=Could not get GPS Last Known Location from database.",
129  "KMLReport.gpsRouteError=Could not extract GPS Route information.",
130  "KMLReport.gpsRouteDatabaseError=Could not get GPS Routes from database.",
131  "KMLReport.gpsSearchDatabaseError=Could not get GPS Searches from database.",
132  "KMLReport.trackpointError=Could not extract Trackpoint information.",
133  "KMLReport.trackpointDatabaseError=Could not get GPS Trackpoints from database.",
134  "KMLReport.stylesheetError=Error placing KML stylesheet. The .KML file will not function properly.",
135  "KMLReport.kmlFileWriteError=Could not write the KML file.",
136  "# {0} - filePath",
137  "KMLReport.errorGeneratingReport=Error adding {0} to case as a report.",
138  "KMLReport.unableToOpenCase=Exception while getting open case.",
139  "Waypoint_Bookmark_Display_String=GPS Bookmark",
140  "Waypoint_Last_Known_Display_String=GPS Last Known Location",
141  "Waypoint_EXIF_Display_String=EXIF Metadata With Location",
142  "Waypoint_Route_Point_Display_String=GPS Individual Route Point",
143  "Waypoint_Search_Display_String=GPS Search",
144  "Waypoint_Trackpoint_Display_String=GPS Trackpoint",
145  "Route_Details_Header=GPS Route"
146  })
147 
148  public void generateReport(String baseReportDir, ReportProgressPanel progressPanel, List<Waypoint> waypointList) {
149  this.waypointList = waypointList;
150  generateReport(baseReportDir, progressPanel);
151  }
152 
153  @Override
154  public void generateReport(String baseReportDir, ReportProgressPanel progressPanel) {
155  try {
156  currentCase = Case.getCurrentCaseThrows();
157  } catch (NoCurrentCaseException ex) {
158  logger.log(Level.SEVERE, "Exception while getting open case.", ex); //NON-NLS
159  return;
160  }
161  // Start the progress bar and setup the report
162  progressPanel.setIndeterminate(true);
163  progressPanel.start();
164  progressPanel.updateStatusLabel(NbBundle.getMessage(this.getClass(), "ReportKML.progress.querying"));
165  String kmlFileFullPath = baseReportDir + REPORT_KML; //NON-NLS
166  String errorMessage = "";
167 
168  skCase = currentCase.getSleuthkitCase();
169 
170  progressPanel.updateStatusLabel(NbBundle.getMessage(this.getClass(), "ReportKML.progress.loading"));
171 
172  Document kmlDocument = setupReportDocument();
173 
175 
176  try {
177  makeRoutes(skCase);
178  addLocationsToReport(skCase, baseReportDir);
179  } catch (GeoLocationDataException | IOException ex) {
180  errorMessage = "Failed to complete report.";
181  logger.log(Level.SEVERE, errorMessage, ex); //NON-NLS
183  }
184 
185  // Copy the style sheet
186  try {
187  InputStream input = getClass().getResourceAsStream(STYLESHEETS_PATH + KML_STYLE_FILE); // Preserve slash direction
188  OutputStream output = new FileOutputStream(baseReportDir + KML_STYLE_FILE); // Preserve slash direction
189  FileUtil.copy(input, output);
190  } catch (IOException ex) {
191  errorMessage = Bundle.KMLReport_stylesheetError();
192  logger.log(Level.SEVERE, errorMessage, ex); //NON-NLS
194  }
195 
196  try (FileOutputStream writer = new FileOutputStream(kmlFileFullPath)) {
197  XMLOutputter outputter = new XMLOutputter(Format.getPrettyFormat());
198  outputter.output(kmlDocument, writer);
199  String prependedStatus = "";
200  if (result == ReportProgressPanel.ReportStatus.ERROR) {
201  prependedStatus = "Incomplete ";
202  }
203  Case.getCurrentCaseThrows().addReport(kmlFileFullPath,
204  NbBundle.getMessage(this.getClass(), "ReportKML.genReport.srcModuleName.text"),
205  prependedStatus + NbBundle.getMessage(this.getClass(), "ReportKML.genReport.reportName"));
206  } catch (IOException ex) {
207  errorMessage = Bundle.KMLReport_kmlFileWriteError();
208  logger.log(Level.SEVERE, errorMessage, ex); //NON-NLS
209  progressPanel.complete(ReportProgressPanel.ReportStatus.ERROR, errorMessage);
210  } catch (TskCoreException ex) {
211  errorMessage = Bundle.KMLReport_errorGeneratingReport(kmlFileFullPath);
212  logger.log(Level.SEVERE, errorMessage, ex);
214  } catch (NoCurrentCaseException ex) {
215  errorMessage = Bundle.KMLReport_unableToOpenCase();
216  logger.log(Level.SEVERE, errorMessage, ex);
218  }
219 
220  progressPanel.complete(result, errorMessage);
221  }
222 
228  private Document setupReportDocument() {
229  ns = Namespace.getNamespace("", "http://www.opengis.net/kml/2.2"); //NON-NLS
230 
231  Element kml = new Element("kml", ns); //NON-NLS
232  kml.addNamespaceDeclaration(Namespace.getNamespace("gx", "http://www.google.com/kml/ext/2.2")); //NON-NLS
233  kml.addNamespaceDeclaration(Namespace.getNamespace("kml", "http://www.opengis.net/kml/2.2")); //NON-NLS
234  kml.addNamespaceDeclaration(Namespace.getNamespace("atom", "http://www.w3.org/2005/Atom")); //NON-NLS
235  Document kmlDocument = new Document(kml);
236 
237  Element document = new Element("Document", ns); //NON-NLS
238  kml.addContent(document);
239 
240  Element name = new Element("name", ns); //NON-NLS
241  ReportBranding rb = new ReportBranding();
242  name.setText(rb.getReportTitle() + " KML"); //NON-NLS
243  document.addContent(name);
244 
245  // Check if ingest has finished
247  Element ingestwarning = new Element("snippet", ns); //NON-NLS
248  ingestwarning.addContent(NbBundle.getMessage(this.getClass(), "ReportBodyFile.ingestWarning.text")); //NON-NLS
249  document.addContent(ingestwarning);
250  }
251 
252  // Create folder structure
253  gpsExifMetadataFolder = new Element("Folder", ns); //NON-NLS
254  CDATA cdataExifMetadataFolder = new CDATA("https://raw.githubusercontent.com/sleuthkit/autopsy/develop/Core/src/org/sleuthkit/autopsy/images/camera-icon-16.png"); //NON-NLS
255  Element hrefExifMetadata = new Element("href", ns).addContent(cdataExifMetadataFolder); //NON-NLS
256  gpsExifMetadataFolder.addContent(new Element("Icon", ns).addContent(hrefExifMetadata)); //NON-NLS
257 
258  gpsBookmarksFolder = new Element("Folder", ns); //NON-NLS
259  CDATA cdataBookmarks = new CDATA("https://raw.githubusercontent.com/sleuthkit/autopsy/develop/Core/src/org/sleuthkit/autopsy/images/gpsfav.png"); //NON-NLS
260  Element hrefBookmarks = new Element("href", ns).addContent(cdataBookmarks); //NON-NLS
261  gpsBookmarksFolder.addContent(new Element("Icon", ns).addContent(hrefBookmarks)); //NON-NLS
262 
263  gpsLastKnownLocationFolder = new Element("Folder", ns); //NON-NLS
264  CDATA cdataLastKnownLocation = new CDATA("https://raw.githubusercontent.com/sleuthkit/autopsy/develop/Core/src/org/sleuthkit/autopsy/images/gps-lastlocation.png"); //NON-NLS
265  Element hrefLastKnownLocation = new Element("href", ns).addContent(cdataLastKnownLocation); //NON-NLS
266  gpsLastKnownLocationFolder.addContent(new Element("Icon", ns).addContent(hrefLastKnownLocation)); //NON-NLS
267 
268  gpsRouteFolder = new Element("Folder", ns); //NON-NLS
269  CDATA cdataRoute = new CDATA("https://raw.githubusercontent.com/sleuthkit/autopsy/develop/Core/src/org/sleuthkit/autopsy/images/gps-trackpoint.png"); //NON-NLS
270  Element hrefRoute = new Element("href", ns).addContent(cdataRoute); //NON-NLS
271  gpsRouteFolder.addContent(new Element("Icon", ns).addContent(hrefRoute)); //NON-NLS
272 
273  gpsSearchesFolder = new Element("Folder", ns); //NON-NLS
274  CDATA cdataSearches = new CDATA("https://raw.githubusercontent.com/sleuthkit/autopsy/develop/Core/src/org/sleuthkit/autopsy/images/gps-search.png"); //NON-NLS
275  Element hrefSearches = new Element("href", ns).addContent(cdataSearches); //NON-NLS
276  gpsSearchesFolder.addContent(new Element("Icon", ns).addContent(hrefSearches)); //NON-NLS
277 
278  gpsTrackpointsFolder = new Element("Folder", ns); //NON-NLS
279  CDATA cdataTrackpoints = new CDATA("https://raw.githubusercontent.com/sleuthkit/autopsy/develop/Core/src/org/sleuthkit/autopsy/images/gps-trackpoint.png"); //NON-NLS
280  Element hrefTrackpoints = new Element("href", ns).addContent(cdataTrackpoints); //NON-NLS
281  gpsTrackpointsFolder.addContent(new Element("Icon", ns).addContent(hrefTrackpoints)); //NON-NLS
282 
283  gpsExifMetadataFolder.addContent(new Element("name", ns).addContent("EXIF Metadata")); //NON-NLS
284  gpsBookmarksFolder.addContent(new Element("name", ns).addContent("GPS Bookmarks")); //NON-NLS
285  gpsLastKnownLocationFolder.addContent(new Element("name", ns).addContent("GPS Last Known Location")); //NON-NLS
286  gpsRouteFolder.addContent(new Element("name", ns).addContent("GPS Routes")); //NON-NLS
287  gpsSearchesFolder.addContent(new Element("name", ns).addContent("GPS Searches")); //NON-NLS
288  gpsTrackpointsFolder.addContent(new Element("name", ns).addContent("GPS Trackpoints")); //NON-NLS
289 
290  document.addContent(gpsExifMetadataFolder);
291  document.addContent(gpsBookmarksFolder);
292  document.addContent(gpsLastKnownLocationFolder);
293  document.addContent(gpsRouteFolder);
294  document.addContent(gpsSearchesFolder);
295  document.addContent(gpsTrackpointsFolder);
296 
297  return kmlDocument;
298  }
299 
309  void addExifMetadataContent(List<Waypoint> points, String baseReportDirectory) throws IOException {
310  for (Waypoint point : points) {
311  Element mapPoint = makePoint(point);
312  if (mapPoint == null) {
313  return;
314  }
315 
316  AbstractFile abstractFile = point.getImage();
317  String details = getFormattedDetails(point, Bundle.Waypoint_EXIF_Display_String());
318 
319  Path path;
320  copyFileUsingStream(abstractFile, Paths.get(baseReportDirectory, abstractFile.getName()).toFile());
321  try {
322  path = Paths.get(removeLeadingImgAndVol(abstractFile.getUniquePath()));
323  } catch (TskCoreException ex) {
324  path = Paths.get(abstractFile.getParentPath(), abstractFile.getName());
325  }
326  if (path == null) {
327  path = Paths.get(abstractFile.getName());
328  }
329 
330  gpsExifMetadataFolder.addContent(makePlacemarkWithPicture(abstractFile.getName(), FeatureColor.RED, details, point.getTimestamp(), mapPoint, path, formattedCoordinates(point.getLatitude(), point.getLongitude())));
331  }
332  }
333 
343  void addLocationsToReport(SleuthkitCase skCase, String baseReportDir) throws GeoLocationDataException, IOException {
344  if (waypointList == null) {
345  addExifMetadataContent(WaypointBuilder.getEXIFWaypoints(skCase), baseReportDir);
346  addWaypoints(WaypointBuilder.getBookmarkWaypoints(skCase), gpsBookmarksFolder, FeatureColor.BLUE, Bundle.Waypoint_Bookmark_Display_String());
347  addWaypoints(WaypointBuilder.getLastKnownWaypoints(skCase), gpsLastKnownLocationFolder, FeatureColor.PURPLE, Bundle.Waypoint_Last_Known_Display_String());
348  addWaypoints(WaypointBuilder.getSearchWaypoints(skCase), gpsSearchesFolder, FeatureColor.WHITE, Bundle.Waypoint_Search_Display_String());
349  addWaypoints(WaypointBuilder.getTrackpointWaypoints(skCase), gpsTrackpointsFolder, FeatureColor.WHITE, Bundle.Waypoint_Trackpoint_Display_String());
350  } else {
351  addExifMetadataContent(WaypointBuilder.getEXIFWaypoints(waypointList), baseReportDir);
352  addWaypoints(WaypointBuilder.getBookmarkWaypoints(waypointList), gpsBookmarksFolder, FeatureColor.BLUE, Bundle.Waypoint_Bookmark_Display_String());
353  addWaypoints(WaypointBuilder.getLastKnownWaypoints(waypointList), gpsLastKnownLocationFolder, FeatureColor.PURPLE, Bundle.Waypoint_Last_Known_Display_String());
354  addWaypoints(WaypointBuilder.getSearchWaypoints(waypointList), gpsSearchesFolder, FeatureColor.WHITE, Bundle.Waypoint_Search_Display_String());
355  addWaypoints(WaypointBuilder.getTrackpointWaypoints(waypointList), gpsTrackpointsFolder, FeatureColor.WHITE, Bundle.Waypoint_Trackpoint_Display_String());
356  }
357  }
358 
367  void addWaypoints(List<Waypoint> points, Element folder, FeatureColor waypointColor, String headerLabel) {
368  for (Waypoint point : points) {
369  addContent(folder, point.getLabel(), waypointColor, getFormattedDetails(point, headerLabel), point.getTimestamp(), makePoint(point), point.getLatitude(), point.getLongitude());
370  }
371  }
372 
385  void addContent(Element folder, String waypointLabel, FeatureColor waypointColor, String formattedDetails, Long timestamp, Element point, Double latitude, Double longitude) {
386  if (folder != null && point != null) {
387  String formattedCords = formattedCoordinates(latitude, longitude);
388  folder.addContent(makePlacemark(waypointLabel, waypointColor, formattedDetails, timestamp, point, formattedCords));
389  }
390  }
391 
399  void makeRoutes(SleuthkitCase skCase) throws GeoLocationDataException {
400  List<Route> routes = null;
401 
402  if (waypointList == null) {
403  routes = Route.getRoutes(skCase);
404  } else {
405  routes = new ArrayList<>();
406  }
407 
408  for (Route route : routes) {
409  addRouteToReport(route);
410  }
411  }
412 
413  void addRouteToReport(Route route) {
414  List<Waypoint> routePoints = route.getRoute();
415  Waypoint start = null;
416  Waypoint end = null;
417  // This is hardcoded knowledge that there is only two points
418  // a start and end. In the long run it would be nice to
419  // support the idea of a route with multiple points. The Route
420  // class supports that idea. Would be nice to figure out how to support
421  // for report.
422  if (routePoints != null && routePoints.size() > 1) {
423  start = routePoints.get(0);
424  end = routePoints.get(1);
425  }
426 
427  if (start == null || end == null) {
428  return;
429  }
430 
431  Element reportRoute = makeLineString(start.getLatitude(), start.getLongitude(), end.getLatitude(), end.getLongitude());
432  Element startingPoint = makePoint(start.getLatitude(), start.getLongitude(), start.getAltitude());
433  Element endingPoint = makePoint(end.getLatitude(), end.getLongitude(), end.getAltitude());
434 
435  String formattedEnd = formattedCoordinates(end.getLatitude(), end.getLongitude());
436  String formattedStart = formattedCoordinates(start.getLatitude(), start.getLongitude());
437 
438  String formattedCoordinates = String.format("%s to %s", formattedStart, formattedEnd);
439 
440  if (reportRoute != null) {
441  gpsRouteFolder.addContent(makePlacemark(route.getLabel(), FeatureColor.GREEN, getFormattedDetails(route), route.getTimestamp(), reportRoute, formattedCoordinates)); //NON-NLS
442  }
443 
444  if (startingPoint != null) {
445  gpsRouteFolder.addContent(makePlacemark(start.getLabel(),
446  FeatureColor.GREEN, getFormattedDetails(start, Bundle.Waypoint_Route_Point_Display_String()),
447  start.getTimestamp(), startingPoint, formattedStart)); //NON-NLS
448  }
449 
450  if (endingPoint != null) {
451  gpsRouteFolder.addContent(makePlacemark(end.getLabel(),
452  FeatureColor.GREEN,
453  getFormattedDetails(end, Bundle.Waypoint_Route_Point_Display_String()),
454  end.getTimestamp(), endingPoint, formattedEnd)); //NON-NLS
455  }
456  }
457 
465  private String getTimeStamp(long timeStamp) {
466  return kmlDateFormat.format(new java.util.Date(timeStamp * 1000));
467  }
468 
476  private Element makePoint(Waypoint point) {
477  return makePoint(point.getLatitude(), point.getLongitude(), point.getAltitude());
478  }
479 
491  private Element makePoint(Double latitude, Double longitude, Double altitude) {
492  if (latitude == null || longitude == null) {
493  return null;
494  }
495 
496  Element point = new Element("Point", ns); //NON-NLS
497 
498  // KML uses lon, lat. Deliberately reversed.1
499  Element coordinates = new Element("coordinates", ns).addContent(longitude + "," + latitude + "," + (altitude != null ? altitude : 0.0)); //NON-NLS
500 
501  if (altitude != null && altitude != 0) {
502  /*
503  * Though we are including a non-zero altitude, clamp it to the
504  * ground because inaccuracies from the GPS data can cause the
505  * terrain to occlude points when zoomed in otherwise. Show the
506  * altitude, but keep the point clamped to the ground. We may change
507  * this later for flying GPS sensors.
508  */
509  Element altitudeMode = new Element("altitudeMode", ns).addContent("clampToGround"); //NON-NLS
510  point.addContent(altitudeMode);
511  }
512  point.addContent(coordinates);
513 
514  return point;
515  }
516 
533  private Element makeLineString(Double startLatitude, Double startLongitude, Double stopLatitude, Double stopLongitude) {
534  if (startLatitude == null || startLongitude == null || stopLatitude == null || stopLongitude == null) {
535  return null;
536  }
537 
538  Element lineString = new Element("LineString", ns); //NON-NLS
539  lineString.addContent(new Element("extrude", ns).addContent("1")); //NON-NLS
540  lineString.addContent(new Element("tessellate", ns).addContent("1")); //NON-NLS
541  lineString.addContent(new Element("altitudeMode", ns).addContent("clampToGround")); //NON-NLS
542  // KML uses lon, lat. Deliberately reversed.
543  lineString.addContent(new Element("coordinates", ns).addContent(
544  startLongitude + "," + startLatitude + ",0.0,"
545  + stopLongitude + "," + stopLatitude + ",0.0")); //NON-NLS
546  return lineString;
547  }
548 
563  private Element makePlacemark(String name, FeatureColor color, String description, Long timestamp, Element feature, String coordinates) {
564  Element placemark = new Element("Placemark", ns); //NON-NLS
565  if (name != null && !name.isEmpty()) {
566  placemark.addContent(new Element("name", ns).addContent(name)); //NON-NLS
567  } else if (timestamp != null) {
568  placemark.addContent(new Element("name", ns).addContent(getTimeStamp(timestamp))); //NON-NLS
569  } else {
570  placemark.addContent(new Element("name", ns).addContent("")); //NON-NLS
571  }
572  placemark.addContent(new Element("styleUrl", ns).addContent(color.getColor())); //NON-NLS
573  placemark.addContent(new Element("description", ns).addContent(description)); //NON-NLS
574  if (timestamp != null) {
575  Element time = new Element("TimeStamp", ns); //NON-NLS
576  time.addContent(new Element("when", ns).addContent(getTimeStamp(timestamp))); //NON-NLS
577  placemark.addContent(time);
578  }
579  placemark.addContent(feature);
580  if (coordinates != null && !coordinates.isEmpty()) {
581  placemark.addContent(new Element("snippet", ns).addContent(coordinates)); //NON-NLS
582  }
583  return placemark;
584  }
585 
601  private Element makePlacemarkWithPicture(String name, FeatureColor color, String description, Long timestamp, Element feature, Path path, String coordinates) {
602  Element placemark = new Element("Placemark", ns); //NON-NLS
603  Element desc = new Element("description", ns); //NON-NLS
604  if (name != null && !name.isEmpty()) {
605  placemark.addContent(new Element("name", ns).addContent(name)); //NON-NLS
606  String image = "<img src='" + name + "' width='400'/>"; //NON-NLS
607  desc.addContent(image);
608  }
609  placemark.addContent(new Element("styleUrl", ns).addContent(color.getColor())); //NON-NLS
610  if (path != null) {
611  String pathAsString = path.toString();
612  if (pathAsString != null && !pathAsString.isEmpty()) {
613  desc.addContent(description + "<b>Source Path:</b> " + pathAsString);
614  }
615  }
616  placemark.addContent(desc);
617 
618  if (timestamp != null) {
619  Element time = new Element("TimeStamp", ns); //NON-NLS
620  time.addContent(new Element("when", ns).addContent(getTimeStamp(timestamp))); //NON-NLS
621  placemark.addContent(time);
622  }
623  placemark.addContent(feature);
624  if (coordinates != null && !coordinates.isEmpty()) {
625  placemark.addContent(new Element("snippet", ns).addContent(coordinates)); //NON-NLS
626  }
627  return placemark;
628  }
629 
640  private void copyFileUsingStream(AbstractFile inputFile, File outputFile) throws ReadContentInputStreamException, IOException {
641  byte[] buffer = new byte[65536];
642  int length;
643  outputFile.createNewFile();
644  try (InputStream is = new ReadContentInputStream(inputFile);
645  OutputStream os = new FileOutputStream(outputFile)) {
646  while ((length = is.read(buffer)) != -1) {
647  os.write(buffer, 0, length);
648  }
649  }
650  }
651 
652  @Override
653  public String getName() {
654  String name = NbBundle.getMessage(this.getClass(), "ReportKML.getName.text");
655  return name;
656  }
657 
658  @Override
659  public String getRelativeFilePath() {
660  return "ReportKML.kml"; //NON-NLS
661  }
662 
663  @Override
664  public String getDescription() {
665  String desc = NbBundle.getMessage(this.getClass(), "ReportKML.getDesc.text");
666  return desc;
667  }
668 
669  @Override
670  public JPanel getConfigurationPanel() {
671  return null; // No configuration panel
672  }
673 
684  private static String removeLeadingImgAndVol(String uniquePath) {
685  // split the path into parts
686  String[] pathSegments = uniquePath.replaceFirst("^/*", "").split("/"); //NON-NLS
687 
688  // Replace image/volume name if they exist in specific entries
689  if (pathSegments.length > 0) {
690  pathSegments[0] = pathSegments[0].replaceFirst("^img_", ""); //NON-NLS
691  }
692  if (pathSegments.length > 1) {
693  pathSegments[1] = pathSegments[1].replaceFirst("^vol_", ""); //NON-NLS
694  }
695 
696  // Assemble the path
697  StringBuilder strbuf = new StringBuilder();
698  for (String segment : pathSegments) {
699  if (!segment.isEmpty()) {
700  strbuf.append("/").append(segment);
701  }
702  }
703  return strbuf.toString();
704  }
705 
714  private String getFormattedDetails(Waypoint point, String header) {
715  StringBuilder result = new StringBuilder(); //NON-NLS
716  result.append(String.format("<h3>%s</h3>", header))
717  .append(formatAttribute("Name", point.getLabel()));
718 
719  Long timestamp = point.getTimestamp();
720  if (timestamp != null) {
721  result.append(formatAttribute("Timestamp", getTimeStamp(timestamp)));
722  }
723 
724  result.append(formatAttribute("Latitude", point.getLatitude().toString()))
725  .append(formatAttribute("Longitude", point.getLongitude().toString()));
726 
727  if (point.getAltitude() != null) {
728  result.append(formatAttribute("Altitude", point.getAltitude().toString()));
729  }
730 
731  List<Waypoint.Property> list = point.getOtherProperties();
732  for (Waypoint.Property prop : list) {
733  String value = prop.getValue();
734  if (value != null && !value.isEmpty()) {
735  result.append(formatAttribute(prop.getDisplayName(), value));
736  }
737  }
738 
739  return result.toString();
740  }
741 
742  private String formatAttribute(String title, String value) {
743  return String.format(HTML_PROP_FORMAT, title, value);
744  }
745 
753  private String getFormattedDetails(Route route) {
754  List<Waypoint> points = route.getRoute();
755  StringBuilder result = new StringBuilder(); //NON-NLS
756 
757  result.append(String.format("<h3>%s</h3>", Bundle.Route_Details_Header()))
758  .append(formatAttribute("Name", route.getLabel()));
759 
760  Long timestamp = route.getTimestamp();
761  if (timestamp != null) {
762  result.append(formatAttribute("Timestamp", getTimeStamp(timestamp)));
763  }
764 
765  if (points.size() > 1) {
766  Waypoint start = points.get(0);
767  Waypoint end = points.get(1);
768 
769  result.append(formatAttribute("Start Latitude", start.getLatitude().toString()))
770  .append(formatAttribute("Start Longitude", start.getLongitude().toString()));
771 
772  Double altitude = start.getAltitude();
773  if (altitude != null) {
774  result.append(formatAttribute("Start Altitude", altitude.toString()));
775  }
776 
777  result.append(formatAttribute("End Latitude", end.getLatitude().toString()))
778  .append(formatAttribute("End Longitude", end.getLongitude().toString()));
779 
780  altitude = end.getAltitude();
781  if (altitude != null) {
782  result.append(formatAttribute("End Altitude", altitude.toString()));
783  }
784  }
785 
786  List<Waypoint.Property> list = route.getOtherProperties();
787  for (Waypoint.Property prop : list) {
788  String value = prop.getValue();
789  if (value != null && !value.isEmpty()) {
790  result.append(formatAttribute(prop.getDisplayName(), value));
791  }
792  }
793 
794  return result.toString();
795  }
796 
805  private String formattedCoordinates(Double latitude, Double longitude) {
806  if (latitude == null || longitude == null) {
807  return "";
808  }
809 
810  return String.format("%.2f, %.2f", latitude, longitude);
811  }
812 }
void copyFileUsingStream(AbstractFile inputFile, File outputFile)
Definition: KMLReport.java:640
Element makeLineString(Double startLatitude, Double startLongitude, Double stopLatitude, Double stopLongitude)
Definition: KMLReport.java:533
static synchronized IngestManager getInstance()
Element makePlacemarkWithPicture(String name, FeatureColor color, String description, Long timestamp, Element feature, Path path, String coordinates)
Definition: KMLReport.java:601
Element makePoint(Double latitude, Double longitude, Double altitude)
Definition: KMLReport.java:491
void addReport(String localPath, String srcModuleName, String reportName)
Definition: Case.java:1630
static String removeLeadingImgAndVol(String uniquePath)
Definition: KMLReport.java:684
static synchronized KMLReport getDefault()
Definition: KMLReport.java:108
List< Waypoint.Property > getOtherProperties()
Definition: Route.java:105
void generateReport(String baseReportDir, ReportProgressPanel progressPanel, List< Waypoint > waypointList)
Definition: KMLReport.java:148
String formatAttribute(String title, String value)
Definition: KMLReport.java:742
Element makePlacemark(String name, FeatureColor color, String description, Long timestamp, Element feature, String coordinates)
Definition: KMLReport.java:563
synchronized static Logger getLogger(String name)
Definition: Logger.java:124
String getFormattedDetails(Waypoint point, String header)
Definition: KMLReport.java:714
void generateReport(String baseReportDir, ReportProgressPanel progressPanel)
Definition: KMLReport.java:154
String formattedCoordinates(Double latitude, Double longitude)
Definition: KMLReport.java:805

Copyright © 2012-2019 Basis Technology. Generated on: Tue Jan 7 2020
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.