Autopsy  4.14.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-2020 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.List;
37 import java.util.logging.Level;
38 import org.jdom2.Document;
39 import org.jdom2.Element;
40 import org.jdom2.Namespace;
41 import org.jdom2.output.Format;
42 import org.jdom2.output.XMLOutputter;
43 import org.jdom2.CDATA;
44 import org.openide.filesystems.FileUtil;
45 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  private Element gpsTracksFolder;
83 
84  private List<Waypoint> waypointList = null;
85 
86  private enum FeatureColor {
87  RED("style.kml#redFeature"),
88  GREEN("style.kml#greenFeature"),
89  BLUE("style.kml#blueFeature"),
90  PURPLE("style.kml#purpleFeature"),
91  WHITE("style.kml#whiteFeature"),
92  YELLOW("style.kml#yellowFeature");
93  private final String color;
94 
95  FeatureColor(String color) {
96  this.color = color;
97  }
98 
99  String getColor() {
100  return this.color;
101  }
102  }
103 
104  // Hidden constructor for the report
105  private KMLReport() {
106  }
107 
108  // Get the default implementation of this report
109  public static synchronized KMLReport getDefault() {
110  if (instance == null) {
111  instance = new KMLReport();
112  }
113  return instance;
114  }
115 
123  @Messages({
124  "KMLReport.unableToExtractPhotos=Could not extract photo information.",
125  "KMLReport.exifPhotoError=Could not extract photos with EXIF metadata.",
126  "KMLReport.bookmarkError=Could not extract Bookmark information.",
127  "KMLReport.gpsBookmarkError=Could not get GPS Bookmarks from database.",
128  "KMLReport.locationError=Could not extract Last Known Location information.",
129  "KMLReport.locationDatabaseError=Could not get GPS Last Known Location from database.",
130  "KMLReport.gpsRouteError=Could not extract GPS Route information.",
131  "KMLReport.gpsRouteDatabaseError=Could not get GPS Routes from database.",
132  "KMLReport.gpsSearchDatabaseError=Could not get GPS Searches from database.",
133  "KMLReport.trackpointError=Could not extract Trackpoint information.",
134  "KMLReport.trackpointDatabaseError=Could not get GPS Trackpoints from database.",
135  "KMLReport.stylesheetError=Error placing KML stylesheet. The .KML file will not function properly.",
136  "KMLReport.kmlFileWriteError=Could not write the KML file.",
137  "# {0} - filePath",
138  "KMLReport.errorGeneratingReport=Error adding {0} to case as a report.",
139  "KMLReport.unableToOpenCase=Exception while getting open case.",
140  "Waypoint_Bookmark_Display_String=GPS Bookmark",
141  "Waypoint_Last_Known_Display_String=GPS Last Known Location",
142  "Waypoint_EXIF_Display_String=EXIF Metadata With Location",
143  "Waypoint_Route_Point_Display_String=GPS Individual Route Point",
144  "Waypoint_Search_Display_String=GPS Search",
145  "Waypoint_Trackpoint_Display_String=GPS Trackpoint",
146  "Waypoint_Track_Display_String=GPS Track",
147  "Route_Details_Header=GPS Route",
148  "ReportBodyFile.ingestWarning.text=Ingest Warning message",
149  "Waypoint_Track_Point_Display_String=GPS Individual Track Point"
150  })
151 
152  public void generateReport(String baseReportDir, ReportProgressPanel progressPanel, List<Waypoint> waypointList) {
153  this.waypointList = waypointList;
154  generateReport(baseReportDir, progressPanel);
155  }
156 
157  @Override
158  public void generateReport(String baseReportDir, ReportProgressPanel progressPanel) {
159  try {
160  currentCase = Case.getCurrentCaseThrows();
161  } catch (NoCurrentCaseException ex) {
162  logger.log(Level.SEVERE, "Exception while getting open case.", ex); //NON-NLS
163  return;
164  }
165  // Start the progress bar and setup the report
166  progressPanel.setIndeterminate(true);
167  progressPanel.start();
168  progressPanel.updateStatusLabel(NbBundle.getMessage(this.getClass(), "ReportKML.progress.querying"));
169  String kmlFileFullPath = baseReportDir + REPORT_KML; //NON-NLS
170  String errorMessage = "";
171 
172  skCase = currentCase.getSleuthkitCase();
173 
174  progressPanel.updateStatusLabel(NbBundle.getMessage(this.getClass(), "ReportKML.progress.loading"));
175 
176  Document kmlDocument = setupReportDocument();
177 
179 
180  try {
181  makeRoutes(skCase);
182  makeTracks(skCase);
183  addLocationsToReport(skCase, baseReportDir);
184  } catch (GeoLocationDataException | IOException ex) {
185  errorMessage = "Failed to complete report.";
186  logger.log(Level.SEVERE, errorMessage, ex); //NON-NLS
188  }
189 
190  // Copy the style sheet
191  try {
192  InputStream input = getClass().getResourceAsStream(STYLESHEETS_PATH + KML_STYLE_FILE); // Preserve slash direction
193  OutputStream output = new FileOutputStream(baseReportDir + KML_STYLE_FILE); // Preserve slash direction
194  FileUtil.copy(input, output);
195  } catch (IOException ex) {
196  errorMessage = Bundle.KMLReport_stylesheetError();
197  logger.log(Level.SEVERE, errorMessage, ex); //NON-NLS
199  }
200 
201  try (FileOutputStream writer = new FileOutputStream(kmlFileFullPath)) {
202  XMLOutputter outputter = new XMLOutputter(Format.getPrettyFormat());
203  outputter.output(kmlDocument, writer);
204  String prependedStatus = "";
205  if (result == ReportProgressPanel.ReportStatus.ERROR) {
206  prependedStatus = "Incomplete ";
207  }
208  Case.getCurrentCaseThrows().addReport(kmlFileFullPath,
209  NbBundle.getMessage(this.getClass(), "ReportKML.genReport.srcModuleName.text"),
210  prependedStatus + NbBundle.getMessage(this.getClass(), "ReportKML.genReport.reportName"));
211  } catch (IOException ex) {
212  errorMessage = Bundle.KMLReport_kmlFileWriteError();
213  logger.log(Level.SEVERE, errorMessage, ex); //NON-NLS
214  progressPanel.complete(ReportProgressPanel.ReportStatus.ERROR, errorMessage);
215  } catch (TskCoreException ex) {
216  errorMessage = Bundle.KMLReport_errorGeneratingReport(kmlFileFullPath);
217  logger.log(Level.SEVERE, errorMessage, ex);
219  } catch (NoCurrentCaseException ex) {
220  errorMessage = Bundle.KMLReport_unableToOpenCase();
221  logger.log(Level.SEVERE, errorMessage, ex);
223  }
224 
225  progressPanel.complete(result, errorMessage);
226  }
227 
233  private Document setupReportDocument() {
234  ns = Namespace.getNamespace("", "http://www.opengis.net/kml/2.2"); //NON-NLS
235 
236  Element kml = new Element("kml", ns); //NON-NLS
237  kml.addNamespaceDeclaration(Namespace.getNamespace("gx", "http://www.google.com/kml/ext/2.2")); //NON-NLS
238  kml.addNamespaceDeclaration(Namespace.getNamespace("kml", "http://www.opengis.net/kml/2.2")); //NON-NLS
239  kml.addNamespaceDeclaration(Namespace.getNamespace("atom", "http://www.w3.org/2005/Atom")); //NON-NLS
240  Document kmlDocument = new Document(kml);
241 
242  Element document = new Element("Document", ns); //NON-NLS
243  kml.addContent(document);
244 
245  Element name = new Element("name", ns); //NON-NLS
246  ReportBranding rb = new ReportBranding();
247  name.setText(rb.getReportTitle() + " KML"); //NON-NLS
248  document.addContent(name);
249 
250  // Check if ingest has finished
252  Element ingestwarning = new Element("snippet", ns); //NON-NLS
253  ingestwarning.addContent(NbBundle.getMessage(this.getClass(), "ReportBodyFile.ingestWarning.text")); //NON-NLS
254  document.addContent(ingestwarning);
255  }
256 
257  // Create folder structure
258  gpsExifMetadataFolder = new Element("Folder", ns); //NON-NLS
259  CDATA cdataExifMetadataFolder = new CDATA("https://raw.githubusercontent.com/sleuthkit/autopsy/develop/Core/src/org/sleuthkit/autopsy/images/camera-icon-16.png"); //NON-NLS
260  Element hrefExifMetadata = new Element("href", ns).addContent(cdataExifMetadataFolder); //NON-NLS
261  gpsExifMetadataFolder.addContent(new Element("Icon", ns).addContent(hrefExifMetadata)); //NON-NLS
262 
263  gpsBookmarksFolder = new Element("Folder", ns); //NON-NLS
264  CDATA cdataBookmarks = new CDATA("https://raw.githubusercontent.com/sleuthkit/autopsy/develop/Core/src/org/sleuthkit/autopsy/images/gpsfav.png"); //NON-NLS
265  Element hrefBookmarks = new Element("href", ns).addContent(cdataBookmarks); //NON-NLS
266  gpsBookmarksFolder.addContent(new Element("Icon", ns).addContent(hrefBookmarks)); //NON-NLS
267 
268  gpsLastKnownLocationFolder = new Element("Folder", ns); //NON-NLS
269  CDATA cdataLastKnownLocation = new CDATA("https://raw.githubusercontent.com/sleuthkit/autopsy/develop/Core/src/org/sleuthkit/autopsy/images/gps-lastlocation.png"); //NON-NLS
270  Element hrefLastKnownLocation = new Element("href", ns).addContent(cdataLastKnownLocation); //NON-NLS
271  gpsLastKnownLocationFolder.addContent(new Element("Icon", ns).addContent(hrefLastKnownLocation)); //NON-NLS
272 
273  gpsRouteFolder = new Element("Folder", ns); //NON-NLS
274  CDATA cdataRoute = new CDATA("https://raw.githubusercontent.com/sleuthkit/autopsy/develop/Core/src/org/sleuthkit/autopsy/images/gps-trackpoint.png"); //NON-NLS
275  Element hrefRoute = new Element("href", ns).addContent(cdataRoute); //NON-NLS
276  gpsRouteFolder.addContent(new Element("Icon", ns).addContent(hrefRoute)); //NON-NLS
277 
278  gpsSearchesFolder = new Element("Folder", ns); //NON-NLS
279  CDATA cdataSearches = new CDATA("https://raw.githubusercontent.com/sleuthkit/autopsy/develop/Core/src/org/sleuthkit/autopsy/images/gps-search.png"); //NON-NLS
280  Element hrefSearches = new Element("href", ns).addContent(cdataSearches); //NON-NLS
281  gpsSearchesFolder.addContent(new Element("Icon", ns).addContent(hrefSearches)); //NON-NLS
282 
283  gpsTrackpointsFolder = new Element("Folder", ns); //NON-NLS
284  CDATA cdataTrackpoints = new CDATA("https://raw.githubusercontent.com/sleuthkit/autopsy/develop/Core/src/org/sleuthkit/autopsy/images/gps-trackpoint.png"); //NON-NLS
285  Element hrefTrackpoints = new Element("href", ns).addContent(cdataTrackpoints); //NON-NLS
286  gpsTrackpointsFolder.addContent(new Element("Icon", ns).addContent(hrefTrackpoints)); //NON-NLS
287 
288  gpsTracksFolder = new Element("Folder", ns); //NON-NLS
289  CDATA cdataTrack = new CDATA("https://raw.githubusercontent.com/sleuthkit/autopsy/develop/Core/src/org/sleuthkit/autopsy/images/gps-trackpoint.png"); //NON-NLS
290  Element hrefTrack = new Element("href", ns).addContent(cdataTrack); //NON-NLS
291  gpsTracksFolder.addContent(new Element("Icon", ns).addContent(hrefTrack)); //NON-NLS
292 
293  gpsExifMetadataFolder.addContent(new Element("name", ns).addContent("EXIF Metadata")); //NON-NLS
294  gpsBookmarksFolder.addContent(new Element("name", ns).addContent("GPS Bookmarks")); //NON-NLS
295  gpsLastKnownLocationFolder.addContent(new Element("name", ns).addContent("GPS Last Known Location")); //NON-NLS
296  gpsRouteFolder.addContent(new Element("name", ns).addContent("GPS Routes")); //NON-NLS
297  gpsSearchesFolder.addContent(new Element("name", ns).addContent("GPS Searches")); //NON-NLS
298  gpsTrackpointsFolder.addContent(new Element("name", ns).addContent("GPS Trackpoints")); //NON-NLS
299  gpsTracksFolder.addContent(new Element("name", ns).addContent("GPS Tracks")); //NON-NLS
300 
301  document.addContent(gpsExifMetadataFolder);
302  document.addContent(gpsBookmarksFolder);
303  document.addContent(gpsLastKnownLocationFolder);
304  document.addContent(gpsRouteFolder);
305  document.addContent(gpsSearchesFolder);
306  document.addContent(gpsTrackpointsFolder);
307  document.addContent(gpsTracksFolder);
308 
309  return kmlDocument;
310  }
311 
321  void addExifMetadataContent(List<Waypoint> points, String baseReportDirectory) throws IOException {
322  for (Waypoint point : points) {
323  Element mapPoint = makePoint(point);
324  if (mapPoint == null) {
325  return;
326  }
327 
328  AbstractFile abstractFile = point.getImage();
329  String details = getFormattedDetails(point, Bundle.Waypoint_EXIF_Display_String());
330 
331  Path path;
332  copyFileUsingStream(abstractFile, Paths.get(baseReportDirectory, abstractFile.getName()).toFile());
333  try {
334  path = Paths.get(removeLeadingImgAndVol(abstractFile.getUniquePath()));
335  } catch (TskCoreException ex) {
336  path = Paths.get(abstractFile.getParentPath(), abstractFile.getName());
337  }
338  if (path == null) {
339  path = Paths.get(abstractFile.getName());
340  }
341 
342  gpsExifMetadataFolder.addContent(makePlacemarkWithPicture(abstractFile.getName(), FeatureColor.RED, details, point.getTimestamp(), mapPoint, path, formattedCoordinates(point.getLatitude(), point.getLongitude())));
343  }
344  }
345 
355  void addLocationsToReport(SleuthkitCase skCase, String baseReportDir) throws GeoLocationDataException, IOException {
356  if (waypointList == null) {
357  addExifMetadataContent(WaypointBuilder.getEXIFWaypoints(skCase), baseReportDir);
358  addWaypoints(WaypointBuilder.getBookmarkWaypoints(skCase), gpsBookmarksFolder, FeatureColor.BLUE, Bundle.Waypoint_Bookmark_Display_String());
359  addWaypoints(WaypointBuilder.getLastKnownWaypoints(skCase), gpsLastKnownLocationFolder, FeatureColor.PURPLE, Bundle.Waypoint_Last_Known_Display_String());
360  addWaypoints(WaypointBuilder.getSearchWaypoints(skCase), gpsSearchesFolder, FeatureColor.WHITE, Bundle.Waypoint_Search_Display_String());
361  addWaypoints(WaypointBuilder.getTrackpointWaypoints(skCase), gpsTrackpointsFolder, FeatureColor.WHITE, Bundle.Waypoint_Trackpoint_Display_String());
362  } else {
363  addExifMetadataContent(WaypointBuilder.getEXIFWaypoints(waypointList), baseReportDir);
364  addWaypoints(WaypointBuilder.getBookmarkWaypoints(waypointList), gpsBookmarksFolder, FeatureColor.BLUE, Bundle.Waypoint_Bookmark_Display_String());
365  addWaypoints(WaypointBuilder.getLastKnownWaypoints(waypointList), gpsLastKnownLocationFolder, FeatureColor.PURPLE, Bundle.Waypoint_Last_Known_Display_String());
366  addWaypoints(WaypointBuilder.getSearchWaypoints(waypointList), gpsSearchesFolder, FeatureColor.WHITE, Bundle.Waypoint_Search_Display_String());
367  addWaypoints(WaypointBuilder.getTrackpointWaypoints(waypointList), gpsTrackpointsFolder, FeatureColor.WHITE, Bundle.Waypoint_Trackpoint_Display_String());
368  }
369  }
370 
379  void addWaypoints(List<Waypoint> points, Element folder, FeatureColor waypointColor, String headerLabel) {
380  for (Waypoint point : points) {
381  addContent(folder, point.getLabel(), waypointColor, getFormattedDetails(point, headerLabel), point.getTimestamp(), makePoint(point), point.getLatitude(), point.getLongitude());
382  }
383  }
384 
397  void addContent(Element folder, String waypointLabel, FeatureColor waypointColor, String formattedDetails, Long timestamp, Element point, Double latitude, Double longitude) {
398  if (folder != null && point != null) {
399  String formattedCords = formattedCoordinates(latitude, longitude);
400  folder.addContent(makePlacemark(waypointLabel, waypointColor, formattedDetails, timestamp, point, formattedCords));
401  }
402  }
403 
411  void makeRoutes(SleuthkitCase skCase) throws GeoLocationDataException {
412  List<Route> routes = null;
413 
414  if (waypointList == null) {
415  routes = Route.getRoutes(skCase);
416  } else {
417  routes = WaypointBuilder.getRoutes(waypointList);
418  }
419 
420  for (Route route : routes) {
421  addRouteToReport(route);
422  }
423  }
424 
430  private void addRouteToReport(Route route) {
431  List<Waypoint> routePoints = route.getRoute();
432  Waypoint start = null;
433  Waypoint end = null;
434  // This is hardcoded knowledge that there is only two points
435  // a start and end. In the long run it would be nice to
436  // support the idea of a route with multiple points. The Route
437  // class supports that idea. Would be nice to figure out how to support
438  // for report.
439  if (routePoints != null && routePoints.size() > 1) {
440  start = routePoints.get(0);
441  end = routePoints.get(1);
442  }
443 
444  if (start == null || end == null) {
445  return;
446  }
447 
448  Element reportRoute = makeLineString(start.getLatitude(), start.getLongitude(), end.getLatitude(), end.getLongitude());
449  Element startingPoint = makePoint(start.getLatitude(), start.getLongitude(), start.getAltitude());
450  Element endingPoint = makePoint(end.getLatitude(), end.getLongitude(), end.getAltitude());
451 
452  String formattedEnd = formattedCoordinates(end.getLatitude(), end.getLongitude());
453  String formattedStart = formattedCoordinates(start.getLatitude(), start.getLongitude());
454 
455  String formattedCoordinates = String.format("%s to %s", formattedStart, formattedEnd);
456 
457  if (reportRoute != null) {
458  gpsRouteFolder.addContent(makePlacemark(route.getLabel(), FeatureColor.GREEN, getFormattedDetails(route), route.getTimestamp(), reportRoute, formattedCoordinates)); //NON-NLS
459  }
460 
461  if (startingPoint != null) {
462  gpsRouteFolder.addContent(makePlacemark(start.getLabel(),
463  FeatureColor.GREEN, getFormattedDetails(start, Bundle.Waypoint_Route_Point_Display_String()),
464  start.getTimestamp(), startingPoint, formattedStart)); //NON-NLS
465  }
466 
467  if (endingPoint != null) {
468  gpsRouteFolder.addContent(makePlacemark(end.getLabel(),
470  getFormattedDetails(end, Bundle.Waypoint_Route_Point_Display_String()),
471  end.getTimestamp(), endingPoint, formattedEnd)); //NON-NLS
472  }
473  }
474 
482  void makeTracks(SleuthkitCase skCase) throws GeoLocationDataException {
483  List<Track> tracks = null;
484 
485  if (waypointList == null) {
486  tracks = Track.getTracks(skCase, null);
487  } else {
488  tracks = WaypointBuilder.getTracks(waypointList);
489  }
490 
491  for (Track track : tracks) {
492  addTrackToReport(track);
493  }
494  }
495 
501  private void addTrackToReport(Track track) {
502  List<Waypoint> trackPoints = track.getPath();
503 
504  // Adding a folder with the track name so that all of the
505  // tracks waypoints with be grouped together.
506  Element trackFolder = new Element("Folder", ns); //NON-NLS
507  trackFolder.addContent(new Element("name", ns).addContent(track.getLabel())); //NON-NLS
508  gpsTracksFolder.addContent(trackFolder);
509 
510  for (Waypoint point : trackPoints) {
511  Element element = makePoint(point.getLatitude(), point.getLongitude(), point.getAltitude());
512  trackFolder.addContent(makePlacemark("",
513  FeatureColor.GREEN, getFormattedDetails(point, Bundle.Waypoint_Track_Point_Display_String()),
514  point.getTimestamp(), element, formattedCoordinates(point.getLatitude(), point.getLongitude()))); //NON-NLS
515  }
516  }
517 
525  private String getTimeStamp(long timeStamp) {
526  return kmlDateFormat.format(new java.util.Date(timeStamp * 1000));
527  }
528 
536  private Element makePoint(Waypoint point) {
537  return makePoint(point.getLatitude(), point.getLongitude(), point.getAltitude());
538  }
539 
551  private Element makePoint(Double latitude, Double longitude, Double altitude) {
552  if (latitude == null || longitude == null) {
553  return null;
554  }
555 
556  Element point = new Element("Point", ns); //NON-NLS
557 
558  // KML uses lon, lat. Deliberately reversed.1
559  Element coordinates = new Element("coordinates", ns).addContent(longitude + "," + latitude + "," + (altitude != null ? altitude : 0.0)); //NON-NLS
560 
561  if (altitude != null && altitude != 0) {
562  /*
563  * Though we are including a non-zero altitude, clamp it to the
564  * ground because inaccuracies from the GPS data can cause the
565  * terrain to occlude points when zoomed in otherwise. Show the
566  * altitude, but keep the point clamped to the ground. We may change
567  * this later for flying GPS sensors.
568  */
569  Element altitudeMode = new Element("altitudeMode", ns).addContent("clampToGround"); //NON-NLS
570  point.addContent(altitudeMode);
571  }
572  point.addContent(coordinates);
573 
574  return point;
575  }
576 
593  private Element makeLineString(Double startLatitude, Double startLongitude, Double stopLatitude, Double stopLongitude) {
594  if (startLatitude == null || startLongitude == null || stopLatitude == null || stopLongitude == null) {
595  return null;
596  }
597 
598  Element lineString = new Element("LineString", ns); //NON-NLS
599  lineString.addContent(new Element("extrude", ns).addContent("1")); //NON-NLS
600  lineString.addContent(new Element("tessellate", ns).addContent("1")); //NON-NLS
601  lineString.addContent(new Element("altitudeMode", ns).addContent("clampToGround")); //NON-NLS
602  // KML uses lon, lat. Deliberately reversed.
603  lineString.addContent(new Element("coordinates", ns).addContent(
604  startLongitude + "," + startLatitude + ",0.0,"
605  + stopLongitude + "," + stopLatitude + ",0.0")); //NON-NLS
606  return lineString;
607  }
608 
623  private Element makePlacemark(String name, FeatureColor color, String description, Long timestamp, Element feature, String coordinates) {
624  Element placemark = new Element("Placemark", ns); //NON-NLS
625  if (name != null && !name.isEmpty()) {
626  placemark.addContent(new Element("name", ns).addContent(name)); //NON-NLS
627  } else if (timestamp != null) {
628  placemark.addContent(new Element("name", ns).addContent(getTimeStamp(timestamp))); //NON-NLS
629  } else {
630  placemark.addContent(new Element("name", ns).addContent("")); //NON-NLS
631  }
632  placemark.addContent(new Element("styleUrl", ns).addContent(color.getColor())); //NON-NLS
633  placemark.addContent(new Element("description", ns).addContent(description)); //NON-NLS
634  if (timestamp != null) {
635  Element time = new Element("TimeStamp", ns); //NON-NLS
636  time.addContent(new Element("when", ns).addContent(getTimeStamp(timestamp))); //NON-NLS
637  placemark.addContent(time);
638  }
639  placemark.addContent(feature);
640  if (coordinates != null && !coordinates.isEmpty()) {
641  placemark.addContent(new Element("snippet", ns).addContent(coordinates)); //NON-NLS
642  }
643  return placemark;
644  }
645 
661  private Element makePlacemarkWithPicture(String name, FeatureColor color, String description, Long timestamp, Element feature, Path path, String coordinates) {
662  Element placemark = new Element("Placemark", ns); //NON-NLS
663  Element desc = new Element("description", ns); //NON-NLS
664  if (name != null && !name.isEmpty()) {
665  placemark.addContent(new Element("name", ns).addContent(name)); //NON-NLS
666  String image = "<img src='" + name + "' width='400'/>"; //NON-NLS
667  desc.addContent(image);
668  }
669  placemark.addContent(new Element("styleUrl", ns).addContent(color.getColor())); //NON-NLS
670  if (path != null) {
671  String pathAsString = path.toString();
672  if (pathAsString != null && !pathAsString.isEmpty()) {
673  desc.addContent(description + "<b>Source Path:</b> " + pathAsString);
674  }
675  }
676  placemark.addContent(desc);
677 
678  if (timestamp != null) {
679  Element time = new Element("TimeStamp", ns); //NON-NLS
680  time.addContent(new Element("when", ns).addContent(getTimeStamp(timestamp))); //NON-NLS
681  placemark.addContent(time);
682  }
683  placemark.addContent(feature);
684  if (coordinates != null && !coordinates.isEmpty()) {
685  placemark.addContent(new Element("snippet", ns).addContent(coordinates)); //NON-NLS
686  }
687  return placemark;
688  }
689 
700  private void copyFileUsingStream(AbstractFile inputFile, File outputFile) throws ReadContentInputStreamException, IOException {
701  byte[] buffer = new byte[65536];
702  int length;
703  outputFile.createNewFile();
704  try (InputStream is = new ReadContentInputStream(inputFile);
705  OutputStream os = new FileOutputStream(outputFile)) {
706  while ((length = is.read(buffer)) != -1) {
707  os.write(buffer, 0, length);
708  }
709  }
710  }
711 
712  @Override
713  public String getName() {
714  String name = NbBundle.getMessage(this.getClass(), "ReportKML.getName.text");
715  return name;
716  }
717 
718  @Override
719  public String getRelativeFilePath() {
720  return "ReportKML.kml"; //NON-NLS
721  }
722 
723  @Override
724  public String getDescription() {
725  String desc = NbBundle.getMessage(this.getClass(), "ReportKML.getDesc.text");
726  return desc;
727  }
728 
729  @Override
730  public JPanel getConfigurationPanel() {
731  return null; // No configuration panel
732  }
733 
744  private static String removeLeadingImgAndVol(String uniquePath) {
745  // split the path into parts
746  String[] pathSegments = uniquePath.replaceFirst("^/*", "").split("/"); //NON-NLS
747 
748  // Replace image/volume name if they exist in specific entries
749  if (pathSegments.length > 0) {
750  pathSegments[0] = pathSegments[0].replaceFirst("^img_", ""); //NON-NLS
751  }
752  if (pathSegments.length > 1) {
753  pathSegments[1] = pathSegments[1].replaceFirst("^vol_", ""); //NON-NLS
754  }
755 
756  // Assemble the path
757  StringBuilder strbuf = new StringBuilder();
758  for (String segment : pathSegments) {
759  if (!segment.isEmpty()) {
760  strbuf.append("/").append(segment);
761  }
762  }
763  return strbuf.toString();
764  }
765 
774  private String getFormattedDetails(Waypoint point, String header) {
775  StringBuilder result = new StringBuilder(); //NON-NLS
776  result.append(String.format("<h3>%s</h3>", header))
777  .append(formatAttribute("Name", point.getLabel()));
778 
779  Long timestamp = point.getTimestamp();
780  if (timestamp != null) {
781  result.append(formatAttribute("Timestamp", getTimeStamp(timestamp)));
782  }
783 
784  result.append(formatAttribute("Latitude", point.getLatitude().toString()))
785  .append(formatAttribute("Longitude", point.getLongitude().toString()));
786 
787  if (point.getAltitude() != null) {
788  result.append(formatAttribute("Altitude", point.getAltitude().toString()));
789  }
790 
791  List<Waypoint.Property> list = point.getOtherProperties();
792  for (Waypoint.Property prop : list) {
793  String value = prop.getValue();
794  if (value != null && !value.isEmpty()) {
795  result.append(formatAttribute(prop.getDisplayName(), value));
796  }
797  }
798 
799  return result.toString();
800  }
801 
810  private String formatAttribute(String title, String value) {
811  return String.format(HTML_PROP_FORMAT, title, value);
812  }
813 
821  private String getFormattedDetails(Route route) {
822  List<Waypoint> points = route.getRoute();
823  StringBuilder result = new StringBuilder(); //NON-NLS
824 
825  result.append(String.format("<h3>%s</h3>", Bundle.Route_Details_Header()))
826  .append(formatAttribute("Name", route.getLabel()));
827 
828  Long timestamp = route.getTimestamp();
829  if (timestamp != null) {
830  result.append(formatAttribute("Timestamp", getTimeStamp(timestamp)));
831  }
832 
833  if (points.size() > 1) {
834  Waypoint start = points.get(0);
835  Waypoint end = points.get(1);
836 
837  result.append(formatAttribute("Start Latitude", start.getLatitude().toString()))
838  .append(formatAttribute("Start Longitude", start.getLongitude().toString()));
839 
840  Double altitude = start.getAltitude();
841  if (altitude != null) {
842  result.append(formatAttribute("Start Altitude", altitude.toString()));
843  }
844 
845  result.append(formatAttribute("End Latitude", end.getLatitude().toString()))
846  .append(formatAttribute("End Longitude", end.getLongitude().toString()));
847 
848  altitude = end.getAltitude();
849  if (altitude != null) {
850  result.append(formatAttribute("End Altitude", altitude.toString()));
851  }
852  }
853 
854  List<Waypoint.Property> list = route.getOtherProperties();
855  for (Waypoint.Property prop : list) {
856  String value = prop.getValue();
857  if (value != null && !value.isEmpty()) {
858  result.append(formatAttribute(prop.getDisplayName(), value));
859  }
860  }
861 
862  return result.toString();
863  }
864 
873  private String formattedCoordinates(Double latitude, Double longitude) {
874  if (latitude == null || longitude == null) {
875  return "";
876  }
877 
878  return String.format("%.2f, %.2f", latitude, longitude);
879  }
880 }
void copyFileUsingStream(AbstractFile inputFile, File outputFile)
Definition: KMLReport.java:700
Element makeLineString(Double startLatitude, Double startLongitude, Double stopLatitude, Double stopLongitude)
Definition: KMLReport.java:593
static synchronized IngestManager getInstance()
static List< Track > getTracks(SleuthkitCase skCase, List<?extends Content > sourceList)
Definition: GeoPath.java:77
Element makePlacemarkWithPicture(String name, FeatureColor color, String description, Long timestamp, Element feature, Path path, String coordinates)
Definition: KMLReport.java:661
Element makePoint(Double latitude, Double longitude, Double altitude)
Definition: KMLReport.java:551
void addReport(String localPath, String srcModuleName, String reportName)
Definition: Case.java:1630
static String removeLeadingImgAndVol(String uniquePath)
Definition: KMLReport.java:744
static synchronized KMLReport getDefault()
Definition: KMLReport.java:109
List< Waypoint.Property > getOtherProperties()
Definition: Route.java:84
void generateReport(String baseReportDir, ReportProgressPanel progressPanel, List< Waypoint > waypointList)
Definition: KMLReport.java:152
String formatAttribute(String title, String value)
Definition: KMLReport.java:810
Element makePlacemark(String name, FeatureColor color, String description, Long timestamp, Element feature, String coordinates)
Definition: KMLReport.java:623
synchronized static Logger getLogger(String name)
Definition: Logger.java:124
String getFormattedDetails(Waypoint point, String header)
Definition: KMLReport.java:774
void generateReport(String baseReportDir, ReportProgressPanel progressPanel)
Definition: KMLReport.java:158
String formattedCoordinates(Double latitude, Double longitude)
Definition: KMLReport.java:873

Copyright © 2012-2020 Basis Technology. Generated on: Wed Apr 8 2020
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.