From 9416819d300ba40f2495e2f38664a137eba5ebca Mon Sep 17 00:00:00 2001 From: Kelly Kelly Date: Wed, 4 Dec 2019 10:09:23 -0500 Subject: [PATCH 1/4] Added KML Report button to Geolocation window --- .../autopsy/geolocation/Bundle.properties | 3 +- .../geolocation/Bundle.properties-MERGED | 6 +- .../geolocation/GeolocationTopComponent.form | 70 +++++-- .../geolocation/GeolocationTopComponent.java | 191 +++++++++++++++--- .../autopsy/geolocation/MapPanel.form | 34 ---- .../autopsy/geolocation/MapPanel.java | 89 ++++---- .../autopsy/geolocation/MapWaypoint.java | 68 ++++--- .../datamodel/BookmarkWaypoint.java | 78 +++++++ .../geolocation/datamodel/Waypoint.java | 45 +---- .../datamodel/WaypointBuilder.java | 131 +++++++++++- .../autopsy/report/modules/kml/KMLReport.java | 125 +++++++----- 11 files changed, 585 insertions(+), 255 deletions(-) create mode 100755 Core/src/org/sleuthkit/autopsy/geolocation/datamodel/BookmarkWaypoint.java diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/Bundle.properties b/Core/src/org/sleuthkit/autopsy/geolocation/Bundle.properties index 84d4ab700c..8330a6ea62 100755 --- a/Core/src/org/sleuthkit/autopsy/geolocation/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/geolocation/Bundle.properties @@ -4,7 +4,6 @@ CTL_GeolocationTopComponent=Geolocation RefreshPanel.refreshLabel.text=The geolocation data has been updated, the visualization may be out of date. RefreshPanel.refreshButton.text=Refresh View RefreshPanel.closeButton.text= -MapPanel.cordLabel.text= WaypointDetailPanel.closeButton.text= WaypointDetailPanel.imageLabel.text= GeoFilterPanel.waypointSettings.border.title= @@ -30,3 +29,5 @@ GeolocationSettingsPanel.osmZipFileField.text= GeolocationSettingsPanel.osmZipFileBrowseButton.text=Browse GeolocationSettingsPanel.serverTestButton.text=Test GeolocationSettingsPanel.osmZipButton.actionCommand=OpenStreeMap tile ZIP file +GeolocationTopComponent.reportButton.text=KML Report +GeolocationTopComponent.coordLabel.text=jLabel1 diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/geolocation/Bundle.properties-MERGED index c69264acae..8ba4bfd036 100755 --- a/Core/src/org/sleuthkit/autopsy/geolocation/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/geolocation/Bundle.properties-MERGED @@ -11,6 +11,9 @@ GeolocationSettingsPanel_osm_server_test_success_message=The provide OSM tile se GeolocationSettingsPanel_osm_server_test_success_message_title=Success GeolocationTC_connection_failure_message=Failed to connect to map title source.\nPlease review map source in Options dialog. GeolocationTC_connection_failure_message_title=Connection Failure +GeolocationTC_empty_waypoint_message=Unable to generate KML report due to a lack of waypoints.\nPlease make sure there are waypoints visible before generating the KML report +GeolocationTC_KML_report_title=KML Report +GeolocationTC_report_progress_title=KML Report Progress GeoTopComponent_filer_data_invalid_msg=Unable to run waypoint filter.\nPlease select one or more data sources. GeoTopComponent_filer_data_invalid_Title=Filter Failure GeoTopComponent_filter_exception_msg=Exception occured during waypoint filtering. @@ -28,7 +31,6 @@ OpenGeolocationAction_name=Geolocation RefreshPanel.refreshLabel.text=The geolocation data has been updated, the visualization may be out of date. RefreshPanel.refreshButton.text=Refresh View RefreshPanel.closeButton.text= -MapPanel.cordLabel.text= WaypointDetailPanel.closeButton.text= WaypointDetailPanel.imageLabel.text= GeoFilterPanel.waypointSettings.border.title= @@ -54,4 +56,6 @@ GeolocationSettingsPanel.osmZipFileField.text= GeolocationSettingsPanel.osmZipFileBrowseButton.text=Browse GeolocationSettingsPanel.serverTestButton.text=Test GeolocationSettingsPanel.osmZipButton.actionCommand=OpenStreeMap tile ZIP file +GeolocationTopComponent.reportButton.text=KML Report +GeolocationTopComponent.coordLabel.text=jLabel1 WaypointExtractAction_label=Extract Files(s) diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/GeolocationTopComponent.form b/Core/src/org/sleuthkit/autopsy/geolocation/GeolocationTopComponent.form index 16cd5368a6..c51c8d496e 100755 --- a/Core/src/org/sleuthkit/autopsy/geolocation/GeolocationTopComponent.form +++ b/Core/src/org/sleuthkit/autopsy/geolocation/GeolocationTopComponent.form @@ -16,6 +16,63 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -24,17 +81,6 @@ - - - - - - - - - - - - \ No newline at end of file + diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/GeolocationTopComponent.java b/Core/src/org/sleuthkit/autopsy/geolocation/GeolocationTopComponent.java index 1bf0aaabbf..0da935b552 100755 --- a/Core/src/org/sleuthkit/autopsy/geolocation/GeolocationTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/geolocation/GeolocationTopComponent.java @@ -21,20 +21,29 @@ package org.sleuthkit.autopsy.geolocation; import java.awt.BorderLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; +import java.io.File; +import java.io.IOException; +import java.text.DateFormat; +import java.text.SimpleDateFormat; import java.util.ArrayList; +import java.util.Date; import java.util.EnumSet; import java.util.List; import java.util.Set; import java.util.logging.Level; import javax.swing.JOptionPane; import javax.swing.SwingUtilities; +import org.openide.filesystems.FileUtil; +import org.openide.util.Exceptions; import org.openide.util.NbBundle.Messages; import org.openide.windows.RetainLocation; import org.openide.windows.TopComponent; import org.openide.windows.WindowManager; import org.sleuthkit.autopsy.casemodule.Case; import static org.sleuthkit.autopsy.casemodule.Case.Events.CURRENT_CASE; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; import org.sleuthkit.autopsy.coreutils.ThreadConfined; @@ -46,6 +55,9 @@ import org.sleuthkit.autopsy.geolocation.datamodel.WaypointBuilder.WaypointFilte import org.sleuthkit.autopsy.ingest.IngestManager; import static org.sleuthkit.autopsy.ingest.IngestManager.IngestModuleEvent.DATA_ADDED; import org.sleuthkit.autopsy.ingest.ModuleDataEvent; +import org.sleuthkit.autopsy.report.ReportModule; +import org.sleuthkit.autopsy.report.ReportProgressPanel; +import org.sleuthkit.autopsy.report.modules.kml.KMLReport; import org.sleuthkit.datamodel.BlackboardArtifact; /** @@ -69,6 +81,11 @@ public final class GeolocationTopComponent extends TopComponent { final RefreshPanel refreshPanel = new RefreshPanel(); + private static final String REPORT_PATH_FMT_STR = "%s" + File.separator + "%s %s %s" + File.separator; + + // This is the hardcoded report name from KMLReport.java + private static final String REPORT_KML = "ReportKML.kml"; + @Messages({ "GLTopComponent_name=Geolocation", "GLTopComponent_initilzation_error=An error occurred during waypoint initilization. Geolocation data maybe incomplete." @@ -80,7 +97,7 @@ public final class GeolocationTopComponent extends TopComponent { @ThreadConfined(type = ThreadConfined.ThreadType.AWT) public GeolocationTopComponent() { initComponents(); - + setName(Bundle.GLTopComponent_name()); this.ingestListener = pce -> { @@ -125,6 +142,20 @@ public final class GeolocationTopComponent extends TopComponent { updateWaypoints(); } }); + + mapPanel.addPropertyChangeListener(MapPanel.CURRENT_MOUSE_GEOPOSITION, new PropertyChangeListener() { + @Override + public void propertyChange(PropertyChangeEvent evt) { + String label = ""; + Object newValue = evt.getNewValue(); + if (newValue != null) { + label = newValue.toString(); + } + + coordLabel.setText(label); + } + + }); } @Override @@ -150,7 +181,7 @@ public final class GeolocationTopComponent extends TopComponent { super.componentOpened(); WindowManager.getDefault().setTopComponentFloating(this, true); } - + @Messages({ "GeolocationTC_connection_failure_message=Failed to connect to map title source.\nPlease review map source in Options dialog.", "GeolocationTC_connection_failure_message_title=Connection Failure" @@ -162,15 +193,15 @@ public final class GeolocationTopComponent extends TopComponent { try { mapPanel.initMap(); } catch (GeoLocationDataException ex) { - JOptionPane.showMessageDialog(this, - Bundle.GeolocationTC_connection_failure_message(), - Bundle.GeolocationTC_connection_failure_message_title(), - JOptionPane.ERROR_MESSAGE); - MessageNotifyUtil.Notify.error( - Bundle.GeolocationTC_connection_failure_message_title(), - Bundle.GeolocationTC_connection_failure_message()); - logger.log(Level.SEVERE, ex.getMessage(), ex); - return; // Doen't set the waypoints. + JOptionPane.showMessageDialog(this, + Bundle.GeolocationTC_connection_failure_message(), + Bundle.GeolocationTC_connection_failure_message_title(), + JOptionPane.ERROR_MESSAGE); + MessageNotifyUtil.Notify.error( + Bundle.GeolocationTC_connection_failure_message_title(), + Bundle.GeolocationTC_connection_failure_message()); + logger.log(Level.SEVERE, ex.getMessage(), ex); + return; // Doen't set the waypoints. } mapPanel.setWaypoints(new ArrayList<>()); updateWaypoints(); @@ -209,20 +240,58 @@ public final class GeolocationTopComponent extends TopComponent { try { filters = geoFilterPanel.getFilterState(); } catch (GeoLocationUIException ex) { - JOptionPane.showMessageDialog(this, - Bundle.GeoTopComponent_filer_data_invalid_msg(), - Bundle.GeoTopComponent_filer_data_invalid_Title(), - JOptionPane.INFORMATION_MESSAGE); + JOptionPane.showMessageDialog(this, + Bundle.GeoTopComponent_filer_data_invalid_msg(), + Bundle.GeoTopComponent_filer_data_invalid_Title(), + JOptionPane.INFORMATION_MESSAGE); return; } - - mapPanel.setWaypointLoading(true); + + setWaypointLoading(true); geoFilterPanel.setEnabled(false); - + Thread thread = new Thread(new WaypointRunner(filters)); thread.start(); } + /** + * Show or hide the waypoint loading progress bar. + * + * @param loading + */ + void setWaypointLoading(boolean loading) { + progressBar.setEnabled(true); + progressBar.setVisible(loading); + progressBar.setString("Loading Waypoints"); + } + + /** + * Create the directory path for the KML report. + * + * This is a modified version of the similar private function from + * KMLReport. + * + * @return Path for the report + * + * @throws IOException + */ + private static String createReportDirectory() throws IOException { + Case currentCase = Case.getCurrentCase(); + + // Create the root reports directory path of the form: /Reports/ / + DateFormat dateFormat = new SimpleDateFormat("MM-dd-yyyy-HH-mm-ss"); + Date date = new Date(); + String dateNoTime = dateFormat.format(date); + String reportPath = String.format(REPORT_PATH_FMT_STR, currentCase.getReportDirectory(), currentCase.getDisplayName(), "Goggle Earth KML", dateNoTime); + // Create the root reports directory. + try { + FileUtil.createFolder(new File(reportPath)); + } catch (IOException ex) { + throw new IOException("Failed to make report folder, unable to generate reports.", ex); + } + return reportPath; + } + /** * This method is called from within the constructor to initialize the form. * WARNING: Do NOT modify this code. The content of this method is always @@ -231,23 +300,88 @@ public final class GeolocationTopComponent extends TopComponent { @SuppressWarnings("unchecked") // //GEN-BEGIN:initComponents private void initComponents() { + java.awt.GridBagConstraints gridBagConstraints; - mapPanel = new org.sleuthkit.autopsy.geolocation.MapPanel(); filterPane = new org.sleuthkit.autopsy.geolocation.HidingPane(); + statusBar = new javax.swing.JPanel(); + reportButton = new javax.swing.JButton(); + progressBar = new javax.swing.JProgressBar(); + coordLabel = new javax.swing.JLabel(); + mapPanel = new org.sleuthkit.autopsy.geolocation.MapPanel(); setLayout(new java.awt.BorderLayout()); + add(filterPane, java.awt.BorderLayout.WEST); - mapPanel.add(filterPane, java.awt.BorderLayout.LINE_START); + statusBar.setLayout(new java.awt.GridBagLayout()); + org.openide.awt.Mnemonics.setLocalizedText(reportButton, org.openide.util.NbBundle.getMessage(GeolocationTopComponent.class, "GeolocationTopComponent.reportButton.text")); // NOI18N + reportButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + reportButtonActionPerformed(evt); + } + }); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 2; + gridBagConstraints.gridy = 0; + gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST; + gridBagConstraints.insets = new java.awt.Insets(5, 5, 5, 5); + statusBar.add(reportButton, gridBagConstraints); + + progressBar.setIndeterminate(true); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 1; + gridBagConstraints.gridy = 0; + gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST; + statusBar.add(progressBar, gridBagConstraints); + + org.openide.awt.Mnemonics.setLocalizedText(coordLabel, org.openide.util.NbBundle.getMessage(GeolocationTopComponent.class, "GeolocationTopComponent.coordLabel.text")); // NOI18N + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 0; + gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST; + gridBagConstraints.weightx = 1.0; + gridBagConstraints.insets = new java.awt.Insets(5, 5, 5, 0); + statusBar.add(coordLabel, gridBagConstraints); + + add(statusBar, java.awt.BorderLayout.SOUTH); add(mapPanel, java.awt.BorderLayout.CENTER); }// //GEN-END:initComponents + @Messages({ + "GeolocationTC_empty_waypoint_message=Unable to generate KML report due to a lack of waypoints.\nPlease make sure there are waypoints visible before generating the KML report", + "GeolocationTC_KML_report_title=KML Report", + "GeolocationTC_report_progress_title=KML Report Progress" + }) + private void reportButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_reportButtonActionPerformed + List visiblePoints = mapPanel.getVisibleWaypoints(); + if (visiblePoints.isEmpty()) { + JOptionPane.showConfirmDialog(this, Bundle.GeolocationTC_empty_waypoint_message(), Bundle.GeolocationTC_KML_report_title(), JOptionPane.OK_OPTION, JOptionPane.INFORMATION_MESSAGE); + return; + } + + try { + ReportProgressPanel progressPanel = new ReportProgressPanel(); + String reportBaseDir = createReportDirectory(); + + progressPanel.setLabels(REPORT_KML, reportBaseDir); + + KMLReport.getDefault().generateReport(reportBaseDir, progressPanel, MapWaypoint.getDataModelWaypoints(visiblePoints)); + JOptionPane.showConfirmDialog(this, progressPanel, Bundle.GeolocationTC_report_progress_title(), JOptionPane.CLOSED_OPTION, JOptionPane.PLAIN_MESSAGE); + } catch (IOException ex) { + logger.log(Level.WARNING, "Unable to create KML report", ex); + } + }//GEN-LAST:event_reportButtonActionPerformed + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JLabel coordLabel; private org.sleuthkit.autopsy.geolocation.HidingPane filterPane; private org.sleuthkit.autopsy.geolocation.MapPanel mapPanel; + private javax.swing.JProgressBar progressBar; + private javax.swing.JButton reportButton; + private javax.swing.JPanel statusBar; // End of variables declaration//GEN-END:variables - + /** * A runnable class for getting waypoints based on the current filters. */ @@ -257,8 +391,8 @@ public final class GeolocationTopComponent extends TopComponent { /** * Constructs the Waypoint Runner - * - * @param filters + * + * @param filters */ WaypointRunner(GeoFilter filters) { this.filters = filters; @@ -274,16 +408,16 @@ public final class GeolocationTopComponent extends TopComponent { filters.getMostRecentNumDays(), filters.showWaypointsWithoutTimeStamp(), new WaypointCallBack()); - + } catch (GeoLocationDataException ex) { logger.log(Level.SEVERE, "Failed to filter waypoints.", ex); SwingUtilities.invokeLater(new Runnable() { @Override public void run() { - JOptionPane.showMessageDialog(GeolocationTopComponent.this, - Bundle.GeoTopComponent_filter_exception_Title(), - Bundle.GeoTopComponent_filter_exception_msg(), - JOptionPane.ERROR_MESSAGE); + JOptionPane.showMessageDialog(GeolocationTopComponent.this, + Bundle.GeoTopComponent_filter_exception_Title(), + Bundle.GeoTopComponent_filter_exception_msg(), + JOptionPane.ERROR_MESSAGE); } }); } @@ -314,6 +448,7 @@ public final class GeolocationTopComponent extends TopComponent { return; } mapPanel.setWaypoints(MapWaypoint.getWaypoints(waypoints)); + setWaypointLoading(false); geoFilterPanel.setEnabled(true); } }); diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/MapPanel.form b/Core/src/org/sleuthkit/autopsy/geolocation/MapPanel.form index 5158982822..bea654be40 100755 --- a/Core/src/org/sleuthkit/autopsy/geolocation/MapPanel.form +++ b/Core/src/org/sleuthkit/autopsy/geolocation/MapPanel.form @@ -91,39 +91,5 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/MapPanel.java b/Core/src/org/sleuthkit/autopsy/geolocation/MapPanel.java index 03ec22cf2b..dd475a84b5 100755 --- a/Core/src/org/sleuthkit/autopsy/geolocation/MapPanel.java +++ b/Core/src/org/sleuthkit/autopsy/geolocation/MapPanel.java @@ -29,6 +29,7 @@ import java.awt.geom.Point2D; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.io.File; +import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.Iterator; @@ -69,6 +70,7 @@ import org.sleuthkit.datamodel.TskCoreException; */ final public class MapPanel extends javax.swing.JPanel { + static final String CURRENT_MOUSE_GEOPOSITION = "CURRENT_MOUSE_GEOPOSITION"; private static final Logger logger = Logger.getLogger(MapPanel.class.getName()); private static final long serialVersionUID = 1L; @@ -122,13 +124,29 @@ final public class MapPanel extends javax.swing.JPanel { Bundle.MapPanel_connection_failure_message_title(), JOptionPane.ERROR_MESSAGE); MessageNotifyUtil.Notify.error( - Bundle.MapPanel_connection_failure_message_title(), - Bundle.MapPanel_connection_failure_message()); + Bundle.MapPanel_connection_failure_message_title(), + Bundle.MapPanel_connection_failure_message()); } } }); } + List getVisibleWaypoints() { + + Rectangle viewport = mapViewer.getViewportBounds(); + List waypoints = new ArrayList<>(); + + Iterator iterator = waypointTree.iterator(); + while (iterator.hasNext()) { + MapWaypoint waypoint = iterator.next(); + if (viewport.contains(mapViewer.getTileFactory().geoToPixel(waypoint.getPosition(), mapViewer.getZoom()))) { + waypoints.add(waypoint); + } + } + + return waypoints; + } + /** * Initialize the map. */ @@ -158,8 +176,8 @@ final public class MapPanel extends javax.swing.JPanel { zoomSlider.setMaximum(tileFactory.getInfo().getMaximumZoomLevel()); setZoom(tileFactory.getInfo().getMaximumZoomLevel() - 1); - - mapViewer.setCenterPosition(new GeoPosition(0,0)); + + mapViewer.setCenterPosition(new GeoPosition(0, 0)); // Basic painters for the way points. WaypointPainter waypointPainter = new WaypointPainter() { @@ -178,17 +196,6 @@ final public class MapPanel extends javax.swing.JPanel { mapViewer.setOverlayPainter(waypointPainter); } - - /** - * Show or hide the waypoint loading progress bar. - * - * @param loading - */ - void setWaypointLoading(boolean loading) { - progressBar.setEnabled(true); - progressBar.setVisible(loading); - progressBar.setString("Loading Waypoints"); - } /** * Setup the zoom slider based on the current tileFactory. @@ -217,15 +224,15 @@ final public class MapPanel extends javax.swing.JPanel { return new VirtualEarthTileFactoryInfo(VirtualEarthTileFactoryInfo.MAP); } } - + /** * Create the TileFactoryInfo for an online OSM tile server. - * - * @param address Tile server address - * + * + * @param address Tile server address + * * @return TileFactoryInfo object for server address. - * - * @throws GeoLocationDataException + * + * @throws GeoLocationDataException */ private TileFactoryInfo createOnlineOSMFactory(String address) throws GeoLocationDataException { if (address.isEmpty()) { @@ -238,15 +245,15 @@ final public class MapPanel extends javax.swing.JPanel { return info; } } - + /** * Create the TileFactoryInfo for OSM zip File - * + * * @param zipPath Path to zip file. - * + * * @return TileFactoryInfo for zip file. - * - * @throws GeoLocationDataException + * + * @throws GeoLocationDataException */ private TileFactoryInfo createOSMZipFactory(String path) throws GeoLocationDataException { if (path.isEmpty()) { @@ -276,7 +283,6 @@ final public class MapPanel extends javax.swing.JPanel { } mapViewer.repaint(); - setWaypointLoading(false); } /** @@ -496,9 +502,6 @@ final public class MapPanel extends javax.swing.JPanel { mapViewer = new org.jxmapviewer.JXMapViewer(); zoomPanel = new javax.swing.JPanel(); zoomSlider = new javax.swing.JSlider(); - infoPanel = new javax.swing.JPanel(); - cordLabel = new javax.swing.JLabel(); - progressBar = new javax.swing.JProgressBar(); setFocusable(false); setLayout(new java.awt.BorderLayout()); @@ -565,27 +568,6 @@ final public class MapPanel extends javax.swing.JPanel { mapViewer.add(zoomPanel, gridBagConstraints); add(mapViewer, java.awt.BorderLayout.CENTER); - - infoPanel.setLayout(new java.awt.GridBagLayout()); - - org.openide.awt.Mnemonics.setLocalizedText(cordLabel, org.openide.util.NbBundle.getMessage(MapPanel.class, "MapPanel.cordLabel.text")); // NOI18N - gridBagConstraints = new java.awt.GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 0; - gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; - gridBagConstraints.weightx = 1.0; - gridBagConstraints.insets = new java.awt.Insets(0, 5, 0, 5); - infoPanel.add(cordLabel, gridBagConstraints); - - progressBar.setIndeterminate(true); - progressBar.setStringPainted(true); - gridBagConstraints = new java.awt.GridBagConstraints(); - gridBagConstraints.gridx = 1; - gridBagConstraints.gridy = 0; - gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHEAST; - infoPanel.add(progressBar, gridBagConstraints); - - add(infoPanel, java.awt.BorderLayout.SOUTH); }// //GEN-END:initComponents private void zoomSliderStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_zoomSliderStateChanged @@ -608,7 +590,7 @@ final public class MapPanel extends javax.swing.JPanel { private void mapViewerMouseMoved(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_mapViewerMouseMoved GeoPosition geopos = mapViewer.getTileFactory().pixelToGeo(evt.getPoint(), mapViewer.getZoom()); - cordLabel.setText(geopos.toString()); + firePropertyChange(CURRENT_MOUSE_GEOPOSITION, null, geopos); }//GEN-LAST:event_mapViewerMouseMoved private void mapViewerMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_mapViewerMouseClicked @@ -618,10 +600,7 @@ final public class MapPanel extends javax.swing.JPanel { // Variables declaration - do not modify//GEN-BEGIN:variables - private javax.swing.JLabel cordLabel; - private javax.swing.JPanel infoPanel; private org.jxmapviewer.JXMapViewer mapViewer; - private javax.swing.JProgressBar progressBar; private javax.swing.JPanel zoomPanel; private javax.swing.JSlider zoomSlider; // End of variables declaration//GEN-END:variables diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/MapWaypoint.java b/Core/src/org/sleuthkit/autopsy/geolocation/MapWaypoint.java index 7a28f49737..61b4800d7a 100755 --- a/Core/src/org/sleuthkit/autopsy/geolocation/MapWaypoint.java +++ b/Core/src/org/sleuthkit/autopsy/geolocation/MapWaypoint.java @@ -95,21 +95,21 @@ final class MapWaypoint extends KdTree.XYZPoint implements org.jxmapviewer.viewe return mapPoints; } - + /** - * Returns a list of of MapWaypoint objects for the given list of + * Returns a list of of MapWaypoint objects for the given list of * datamodel.Waypoint objects. - * + * * @param dmWaypoints - * - * @return List of MapWaypoint objects. List will be empty if dmWaypoints was - * empty or null. + * + * @return List of MapWaypoint objects. List will be empty if dmWaypoints + * was empty or null. */ static List getWaypoints(List dmWaypoints) { List mapPoints = new ArrayList<>(); if (dmWaypoints != null) { - + for (Waypoint point : dmWaypoints) { mapPoints.add(new MapWaypoint(point)); } @@ -118,6 +118,27 @@ final class MapWaypoint extends KdTree.XYZPoint implements org.jxmapviewer.viewe return mapPoints; } + /** + * Helper function to get a list of data model waypoints from a list of + * MapWaypoints. + * + * @param mapWaypoints + * + * @return A list of Waypoint objects, or empty list if mapWaypoints was + * null or empty. + */ + static List getDataModelWaypoints(List mapWaypoints) { + List waypoints = new ArrayList<>(); + + if (mapWaypoints != null) { + for (MapWaypoint point : mapWaypoints) { + waypoints.add(point.dataModelWaypoint); + } + } + + return waypoints; + } + /** * Returns a MapWaypoint without a reference to the datamodel waypoint. * @@ -271,20 +292,20 @@ final class MapWaypoint extends KdTree.XYZPoint implements org.jxmapviewer.viewe } return menuItems; } - - /** + + /** * Get the nicely formatted details for the given waypoint. - * - * @param point Waypoint object + * + * @param point Waypoint object * @param header String details header - * - * @return HTML formatted String of details for given waypoint + * + * @return HTML formatted String of details for given waypoint */ private String getFormattedDetails(Waypoint point) { StringBuilder result = new StringBuilder(); //NON-NLS - + result.append("").append(formatAttribute("Name", point.getLabel())); - + Long timestamp = point.getTimestamp(); if (timestamp != null) { result.append(formatAttribute("Timestamp", getTimeStamp(timestamp))); @@ -292,19 +313,19 @@ final class MapWaypoint extends KdTree.XYZPoint implements org.jxmapviewer.viewe result.append(formatAttribute("Latitude", point.getLatitude().toString())) .append(formatAttribute("Longitude", point.getLongitude().toString())); - + if (point.getAltitude() != null) { result.append(formatAttribute("Altitude", point.getAltitude().toString())); } List list = point.getOtherProperties(); - for(Waypoint.Property prop: list) { + for (Waypoint.Property prop : list) { String value = prop.getValue(); - if(value != null && !value.isEmpty()) { + if (value != null && !value.isEmpty()) { result.append(formatAttribute(prop.getDisplayName(), value)); } } - + result.append(""); return result.toString(); @@ -312,16 +333,16 @@ final class MapWaypoint extends KdTree.XYZPoint implements org.jxmapviewer.viewe /** * Format a title value pair. - * + * * @param title Title of the property * @param value Value of the property - * - * @return Formatted string with the title and value + * + * @return Formatted string with the title and value */ private String formatAttribute(String title, String value) { return String.format(HTML_PROP_FORMAT, title, value); } - + /** * Format a point time stamp (in seconds) to the report format. * @@ -333,7 +354,6 @@ final class MapWaypoint extends KdTree.XYZPoint implements org.jxmapviewer.viewe return DATE_FORMAT.format(new java.util.Date(timeStamp * 1000)); } - /** * An action class for Extracting artifact files. */ diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/BookmarkWaypoint.java b/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/BookmarkWaypoint.java new file mode 100755 index 0000000000..08fdfeccb6 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/BookmarkWaypoint.java @@ -0,0 +1,78 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2019 Basis Technology Corp. + * contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.geolocation.datamodel; + +import java.util.Map; +import org.sleuthkit.datamodel.BlackboardArtifact; +import org.sleuthkit.datamodel.BlackboardAttribute; + +/** + * Class to represent TSK_GPS_BOOKMARK waypoints + * + */ +final class BookmarkWaypoint extends Waypoint { + + /** + * Constructs a new BookmarkWaypoint from the given artifact. + * + * @param artifact BlackboardArtifact for this waypoint + * + * @throws GeoLocationDataException + */ + BookmarkWaypoint(BlackboardArtifact artifact) throws GeoLocationDataException { + this(artifact, getAttributesFromArtifactAsMap(artifact)); + } + + /** + * Constructs a new BookmarkWaypoint. + * + * @param artifact BlackboardArtifact for this waypoint + * @param attributeMap A Map of the BlackboardAttributes for the given + * artifact. + * + * @throws GeoLocationDataException + */ + private BookmarkWaypoint(BlackboardArtifact artifact, Map attributeMap) throws GeoLocationDataException { + super(artifact, + getLabelFromArtifact(attributeMap), + attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME) != null ? attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME).getValueLong() : null, + attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE) != null ? attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE).getValueDouble() : null, + attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE) != null ? attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE).getValueDouble() : null, + attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_ALTITUDE) != null ? attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_ALTITUDE).getValueDouble() : null, + null, attributeMap, null); + } + + /** + * Gets the label for this waypoint. + * + * @param artifact BlackboardArtifact for waypoint + * + * @return Returns a label for the waypoint, or empty string if no label was + * found. + */ + private static String getLabelFromArtifact(Map attributeMap) { + BlackboardAttribute attribute = attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME); + if (attribute != null) { + return attribute.getDisplayString(); + } + + return ""; + } + +} diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/Waypoint.java b/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/Waypoint.java index 27e5da0dbd..8cc35555eb 100755 --- a/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/Waypoint.java +++ b/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/Waypoint.java @@ -50,10 +50,10 @@ public class Waypoint { final private List immutablePropertiesList; /** - * This is a list of attributes that are already being handled by the - * by getter functions. + * This is a list of attributes that are already being handled by the by + * getter functions. */ - static private BlackboardAttribute.ATTRIBUTE_TYPE[] ALREADY_HANDLED_ATTRIBUTES = { + static final private BlackboardAttribute.ATTRIBUTE_TYPE[] ALREADY_HANDLED_ATTRIBUTES = { BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE, @@ -65,19 +65,6 @@ public class Waypoint { BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE_END, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE_END,}; - /** - * Construct a waypoint with the given artifact. - * - * @param artifact BlackboardArtifact for this waypoint - * - * @throws GeoLocationDataException Exception will be thrown if artifact did - * not have a valid longitude and latitude. - */ - Waypoint(BlackboardArtifact artifact) throws GeoLocationDataException { - this(artifact, - getAttributesFromArtifactAsMap(artifact)); - } - /** * Constructor that initializes all of the member variables. * @@ -110,25 +97,6 @@ public class Waypoint { immutablePropertiesList = Collections.unmodifiableList(createGeolocationProperties(attributeMap)); } - /** - * Constructs a new ArtifactWaypoint. - * - * @param artifact BlackboardArtifact for this waypoint - * @param attributeMap A Map of the BlackboardAttributes for the given - * artifact. - * - * @throws GeoLocationDataException - */ - private Waypoint(BlackboardArtifact artifact, Map attributeMap) throws GeoLocationDataException { - this(artifact, - getLabelFromArtifact(attributeMap), - attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME) != null ? attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME).getValueLong() : null, - attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE) != null ? attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE).getValueDouble() : null, - attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE) != null ? attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE).getValueDouble() : null, - attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_ALTITUDE) != null ? attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_ALTITUDE).getValueDouble() : null, - null, attributeMap, null); - } - /** * Get the BlackboardArtifact that this waypoint represents. * @@ -205,11 +173,12 @@ public class Waypoint { public List getOtherProperties() { return immutablePropertiesList; } - + /** * Returns the route that this waypoint is apart of . - * - * @return The waypoint route or null if the waypoint is not apart of a route. + * + * @return The waypoint route or null if the waypoint is not apart of a + * route. */ public Route getRoute() { return route; diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/WaypointBuilder.java b/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/WaypointBuilder.java index 7189302809..86539412be 100755 --- a/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/WaypointBuilder.java +++ b/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/WaypointBuilder.java @@ -45,7 +45,7 @@ public final class WaypointBuilder { = "SELECT artifact_id, artifact_type_id " + "FROM blackboard_attributes " + "WHERE attribute_type_id IN (%d, %d) "; //NON-NLS - + // SELECT statement to get only artifact_ids final static String GEO_ARTIFACT_QUERY_ID_ONLY = "SELECT artifact_id " @@ -70,8 +70,8 @@ public final class WaypointBuilder { + " )"; // Returns a list of artifacts with no time stamp - final static String SELECT_WO_TIMESTAMP = - "SELECT DISTINCT artifact_id, artifact_type_id " + final static String SELECT_WO_TIMESTAMP + = "SELECT DISTINCT artifact_id, artifact_type_id " + "FROM blackboard_attributes " + "WHERE artifact_id NOT IN (%s) " + "AND artifact_id IN (%s)"; //NON-NLS @@ -121,6 +121,25 @@ public final class WaypointBuilder { return points; } + /** + * Returns a list of routes from the given list of waypoints. + * + * @param waypoints A list of waypoints + * + * @return A list of routes or an empty list if none were found. + */ + public static List getRoutes(List waypoints) { + List routeList = new ArrayList<>(); + for (Waypoint point : waypoints) { + Route route = point.getRoute(); + if (route != null && !routeList.contains(route)) { + routeList.add(route); + } + } + + return routeList; + } + /** * Gets a list of Waypoints for TSK_GPS_TRACKPOINT artifacts. * @@ -150,6 +169,25 @@ public final class WaypointBuilder { return points; } + /** + * Returns a list of waypoints that come from TSK_GEO_TRACKPOINT artifacts. + * + * @param waypoints A list of waypoints + * + * @return A list of trackpoint waypoints or empty list if none were found. + */ + public static List getTrackpointWaypoints(List waypoints) { + List specificPoints = new ArrayList<>(); + + for (Waypoint point : waypoints) { + if (point instanceof TrackpointWaypoint) { + specificPoints.add(point); + } + } + + return specificPoints; + } + /** * Gets a list of Waypoints for TSK_METADATA_EXIF artifacts. * @@ -183,6 +221,25 @@ public final class WaypointBuilder { return points; } + /** + * Returns a list of waypoints that come from TSK_METADATA_EXIF artifacts. + * + * @param waypoints A list of waypoints + * + * @return A list of trackpoint waypoints or empty list if none were found. + */ + public static List getEXIFWaypoints(List waypoints) { + List specificPoints = new ArrayList<>(); + + for (Waypoint point : waypoints) { + if (point instanceof EXIFWaypoint) { + specificPoints.add(point); + } + } + + return specificPoints; + } + /** * Gets a list of Waypoints for TSK_GPS_SEARCH artifacts. * @@ -214,6 +271,25 @@ public final class WaypointBuilder { return points; } + /** + * Returns a list of waypoints that come from TSK_GPS_SEARCH artifacts. + * + * @param waypoints A list of waypoints + * + * @return A list of trackpoint waypoints or empty list if none were found. + */ + public static List getSearchWaypoints(List waypoints) { + List specificPoints = new ArrayList<>(); + + for (Waypoint point : waypoints) { + if (point instanceof SearchWaypoint) { + specificPoints.add(point); + } + } + + return specificPoints; + } + /** * Gets a list of Waypoints for TSK_GPS_LAST_KNOWN_LOCATION artifacts. * @@ -245,6 +321,26 @@ public final class WaypointBuilder { return points; } + /** + * Returns a list of waypoints that come from TSK_GPS_LAST_KNOWN_LOCATION + * artifacts. + * + * @param waypoints A list of waypoints + * + * @return A list of trackpoint waypoints or empty list if none were found. + */ + public static List getLastKnownWaypoints(List waypoints) { + List specificPoints = new ArrayList<>(); + + for (Waypoint point : waypoints) { + if (point instanceof LastKnownWaypoint) { + specificPoints.add(point); + } + } + + return specificPoints; + } + /** * Gets a list of Waypoints for TSK_GPS_BOOKMARK artifacts. * @@ -266,7 +362,7 @@ public final class WaypointBuilder { if (artifacts != null) { for (BlackboardArtifact artifact : artifacts) { try { - Waypoint point = new Waypoint(artifact); + Waypoint point = new BookmarkWaypoint(artifact); points.add(point); } catch (GeoLocationDataException ex) { logger.log(Level.WARNING, String.format("No longitude or latitude available for TSK_GPS_BOOKMARK artifactID: %d", artifact.getArtifactID()), ex);//NON-NLS @@ -276,6 +372,26 @@ public final class WaypointBuilder { return points; } + /** + * Returns a list of waypoints that come from TSK_GPS_LAST_KNOWN_LOCATION + * artifacts. + * + * @param waypoints A list of waypoints + * + * @return A list of trackpoint waypoints or empty list if none were found. + */ + public static List getBookmarkWaypoints(List waypoints) { + List specificPoints = new ArrayList<>(); + + for (Waypoint point : waypoints) { + if (point instanceof BookmarkWaypoint) { + specificPoints.add(point); + } + } + + return specificPoints; + } + /** * Get a filtered list of waypoints. * @@ -386,7 +502,7 @@ public final class WaypointBuilder { String mostRecentQuery = ""; if (!showAll && cntDaysFromRecent > 0) { - mostRecentQuery = String.format("AND value_int64 > (%s)", //NON-NLS + mostRecentQuery = String.format("AND value_int64 > (%s)", //NON-NLS String.format(MOST_RECENT_TIME, cntDaysFromRecent, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME.getTypeID(), @@ -464,7 +580,7 @@ public final class WaypointBuilder { waypoints.add(new EXIFWaypoint(artifact)); break; case TSK_GPS_BOOKMARK: - waypoints.add(new Waypoint(artifact)); + waypoints.add(new BookmarkWaypoint(artifact)); break; case TSK_GPS_TRACKPOINT: waypoints.add(new TrackpointWaypoint(artifact)); @@ -477,8 +593,7 @@ public final class WaypointBuilder { waypoints.addAll(route.getRoute()); break; default: - waypoints.add(new Waypoint(artifact)); - break; + throw new GeoLocationDataException(String.format("Unable to create waypoint for artifact of type %s", type.toString())); } return waypoints; diff --git a/Core/src/org/sleuthkit/autopsy/report/modules/kml/KMLReport.java b/Core/src/org/sleuthkit/autopsy/report/modules/kml/KMLReport.java index a140338524..e7e7a49503 100644 --- a/Core/src/org/sleuthkit/autopsy/report/modules/kml/KMLReport.java +++ b/Core/src/org/sleuthkit/autopsy/report/modules/kml/KMLReport.java @@ -33,6 +33,7 @@ import java.io.OutputStream; import java.nio.file.Path; import java.nio.file.Paths; import java.text.SimpleDateFormat; +import java.util.ArrayList; import java.util.List; import java.util.logging.Level; import org.jdom2.Document; @@ -59,7 +60,7 @@ import org.sleuthkit.datamodel.TskCoreException; /** * Generates a KML file based on geospatial information from the BlackBoard. */ -class KMLReport implements GeneralReportModule { +public class KMLReport implements GeneralReportModule { private static final Logger logger = Logger.getLogger(KMLReport.class.getName()); private static final String KML_STYLE_FILE = "style.kml"; @@ -79,6 +80,8 @@ class KMLReport implements GeneralReportModule { private Element gpsSearchesFolder; private Element gpsTrackpointsFolder; + private List waypointList = null; + private enum FeatureColor { RED("style.kml#redFeature"), GREEN("style.kml#greenFeature"), @@ -141,6 +144,11 @@ class KMLReport implements GeneralReportModule { "Route_Details_Header=GPS Route" }) + public void generateReport(String baseReportDir, ReportProgressPanel progressPanel, List waypointList) { + this.waypointList = waypointList; + generateReport(baseReportDir, progressPanel); + } + @Override public void generateReport(String baseReportDir, ReportProgressPanel progressPanel) { try { @@ -298,7 +306,7 @@ class KMLReport implements GeneralReportModule { * @throws IOException */ void addExifMetadataContent(List points, String baseReportDirectory) throws IOException { - for(Waypoint point: points) { + for (Waypoint point : points) { Element mapPoint = makePoint(point); if (mapPoint == null) { return; @@ -332,41 +340,49 @@ class KMLReport implements GeneralReportModule { * @throws IOException */ void addLocationsToReport(SleuthkitCase skCase, String baseReportDir) throws GeoLocationDataException, IOException { - addExifMetadataContent(WaypointBuilder.getEXIFWaypoints(skCase), baseReportDir); - addWaypoints(WaypointBuilder.getBookmarkWaypoints(skCase), gpsBookmarksFolder, FeatureColor.BLUE, Bundle.Waypoint_Bookmark_Display_String()); - addWaypoints(WaypointBuilder.getLastKnownWaypoints(skCase), gpsLastKnownLocationFolder, FeatureColor.PURPLE, Bundle.Waypoint_Last_Known_Display_String()); - addWaypoints(WaypointBuilder.getSearchWaypoints(skCase), gpsSearchesFolder, FeatureColor.WHITE, Bundle.Waypoint_Search_Display_String()); - addWaypoints(WaypointBuilder.getTrackpointWaypoints(skCase), gpsTrackpointsFolder, FeatureColor.WHITE, Bundle.Waypoint_Trackpoint_Display_String()); + if (waypointList == null) { + addExifMetadataContent(WaypointBuilder.getEXIFWaypoints(skCase), baseReportDir); + addWaypoints(WaypointBuilder.getBookmarkWaypoints(skCase), gpsBookmarksFolder, FeatureColor.BLUE, Bundle.Waypoint_Bookmark_Display_String()); + addWaypoints(WaypointBuilder.getLastKnownWaypoints(skCase), gpsLastKnownLocationFolder, FeatureColor.PURPLE, Bundle.Waypoint_Last_Known_Display_String()); + addWaypoints(WaypointBuilder.getSearchWaypoints(skCase), gpsSearchesFolder, FeatureColor.WHITE, Bundle.Waypoint_Search_Display_String()); + addWaypoints(WaypointBuilder.getTrackpointWaypoints(skCase), gpsTrackpointsFolder, FeatureColor.WHITE, Bundle.Waypoint_Trackpoint_Display_String()); + } else { + addExifMetadataContent(WaypointBuilder.getEXIFWaypoints(waypointList), baseReportDir); + addWaypoints(WaypointBuilder.getBookmarkWaypoints(waypointList), gpsBookmarksFolder, FeatureColor.BLUE, Bundle.Waypoint_Bookmark_Display_String()); + addWaypoints(WaypointBuilder.getLastKnownWaypoints(waypointList), gpsLastKnownLocationFolder, FeatureColor.PURPLE, Bundle.Waypoint_Last_Known_Display_String()); + addWaypoints(WaypointBuilder.getSearchWaypoints(waypointList), gpsSearchesFolder, FeatureColor.WHITE, Bundle.Waypoint_Search_Display_String()); + addWaypoints(WaypointBuilder.getTrackpointWaypoints(waypointList), gpsTrackpointsFolder, FeatureColor.WHITE, Bundle.Waypoint_Trackpoint_Display_String()); + } } - + /** - * For each point in the waypoint list an Element to represent the given waypoint - * is created and added it to the given Element folder. - * - * @param points List of waypoints to add to the report - * @param folder The Element folder to add the points to + * For each point in the waypoint list an Element to represent the given + * waypoint is created and added it to the given Element folder. + * + * @param points List of waypoints to add to the report + * @param folder The Element folder to add the points to * @param waypointColor The color the waypoint should appear in the report */ void addWaypoints(List points, Element folder, FeatureColor waypointColor, String headerLabel) { - for(Waypoint point: points) { + for (Waypoint point : points) { addContent(folder, point.getLabel(), waypointColor, getFormattedDetails(point, headerLabel), point.getTimestamp(), makePoint(point), point.getLatitude(), point.getLongitude()); } } - + /** * Adds the waypoint Element with details to the report in the given folder. - * - * @param folder Element folder to add the waypoint to - * @param waypointLabel String waypoint Label - * @param waypointColor FeatureColor for the waypoint + * + * @param folder Element folder to add the waypoint to + * @param waypointLabel String waypoint Label + * @param waypointColor FeatureColor for the waypoint * @param formattedDetails String HTML formatted waypoint details - * @param timestamp Long timestamp (unix\jave epoch seconds) - * @param point Element point object - * @param latitude Double latitude value - * @param longitude Double longitude value + * @param timestamp Long timestamp (unix\jave epoch seconds) + * @param point Element point object + * @param latitude Double latitude value + * @param longitude Double longitude value */ void addContent(Element folder, String waypointLabel, FeatureColor waypointColor, String formattedDetails, Long timestamp, Element point, Double latitude, Double longitude) { - if(folder != null && point != null) { + if (folder != null && point != null) { String formattedCords = formattedCoordinates(latitude, longitude); folder.addContent(makePlacemark(waypointLabel, waypointColor, formattedDetails, timestamp, point, formattedCords)); } @@ -380,17 +396,19 @@ class KMLReport implements GeneralReportModule { * @throws TskCoreException */ void makeRoutes(SleuthkitCase skCase) throws GeoLocationDataException { - List routes = Route.getRoutes(skCase); - - if(routes == null) { - return; + List routes = null; + + if (waypointList == null) { + routes = Route.getRoutes(skCase); + } else { + routes = new ArrayList<>(); } for (Route route : routes) { addRouteToReport(route); } } - + void addRouteToReport(Route route) { List routePoints = route.getRoute(); Waypoint start = null; @@ -423,15 +441,15 @@ class KMLReport implements GeneralReportModule { } if (startingPoint != null) { - gpsRouteFolder.addContent(makePlacemark(start.getLabel(), - FeatureColor.GREEN, getFormattedDetails(start, Bundle.Waypoint_Route_Point_Display_String()), + gpsRouteFolder.addContent(makePlacemark(start.getLabel(), + FeatureColor.GREEN, getFormattedDetails(start, Bundle.Waypoint_Route_Point_Display_String()), start.getTimestamp(), startingPoint, formattedStart)); //NON-NLS } if (endingPoint != null) { - gpsRouteFolder.addContent(makePlacemark(end.getLabel(), - FeatureColor.GREEN, - getFormattedDetails(end, Bundle.Waypoint_Route_Point_Display_String()), + gpsRouteFolder.addContent(makePlacemark(end.getLabel(), + FeatureColor.GREEN, + getFormattedDetails(end, Bundle.Waypoint_Route_Point_Display_String()), end.getTimestamp(), endingPoint, formattedEnd)); //NON-NLS } } @@ -688,11 +706,11 @@ class KMLReport implements GeneralReportModule { /** * Get the nicely formatted details for the given waypoint. - * - * @param point Waypoint object + * + * @param point Waypoint object * @param header String details header - * - * @return HTML formatted String of details for given waypoint + * + * @return HTML formatted String of details for given waypoint */ private String getFormattedDetails(Waypoint point, String header) { StringBuilder result = new StringBuilder(); //NON-NLS @@ -706,15 +724,15 @@ class KMLReport implements GeneralReportModule { result.append(formatAttribute("Latitude", point.getLatitude().toString())) .append(formatAttribute("Longitude", point.getLongitude().toString())); - + if (point.getAltitude() != null) { result.append(formatAttribute("Altitude", point.getAltitude().toString())); } List list = point.getOtherProperties(); - for(Waypoint.Property prop: list) { + for (Waypoint.Property prop : list) { String value = prop.getValue(); - if(value != null && !value.isEmpty()) { + if (value != null && !value.isEmpty()) { result.append(formatAttribute(prop.getDisplayName(), value)); } } @@ -733,8 +751,7 @@ class KMLReport implements GeneralReportModule { * * @return A HTML formatted list of the Route attributes */ - - private String getFormattedDetails(Route route) { + private String getFormattedDetails(Route route) { List points = route.getRoute(); StringBuilder result = new StringBuilder(); //NON-NLS @@ -752,38 +769,38 @@ class KMLReport implements GeneralReportModule { result.append(formatAttribute("Start Latitude", start.getLatitude().toString())) .append(formatAttribute("Start Longitude", start.getLongitude().toString())); - + Double altitude = start.getAltitude(); - if(altitude != null) { + if (altitude != null) { result.append(formatAttribute("Start Altitude", altitude.toString())); } - + result.append(formatAttribute("End Latitude", end.getLatitude().toString())) .append(formatAttribute("End Longitude", end.getLongitude().toString())); - + altitude = end.getAltitude(); - if(altitude != null) { + if (altitude != null) { result.append(formatAttribute("End Altitude", altitude.toString())); } } List list = route.getOtherProperties(); - for(Waypoint.Property prop: list) { + for (Waypoint.Property prop : list) { String value = prop.getValue(); - if(value != null && !value.isEmpty()) { + if (value != null && !value.isEmpty()) { result.append(formatAttribute(prop.getDisplayName(), value)); } } return result.toString(); } - + /** * Helper functions for consistently formatting longitude and latitude. - * - * @param latitude Double latitude value + * + * @param latitude Double latitude value * @param longitude Double longitude value - * + * * @return String Nicely formatted double values separated by a comma */ private String formattedCoordinates(Double latitude, Double longitude) { From 2f5f106cab31fe0bc6e68adfb07ffc46468d57d1 Mon Sep 17 00:00:00 2001 From: Kelly Kelly Date: Wed, 4 Dec 2019 13:34:08 -0500 Subject: [PATCH 2/4] Fixed codacy issues --- .../autopsy/geolocation/GeolocationTopComponent.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/GeolocationTopComponent.java b/Core/src/org/sleuthkit/autopsy/geolocation/GeolocationTopComponent.java index eb22344a2b..d810664c5a 100755 --- a/Core/src/org/sleuthkit/autopsy/geolocation/GeolocationTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/geolocation/GeolocationTopComponent.java @@ -31,7 +31,9 @@ import java.util.ArrayList; import java.util.Date; import java.util.EnumSet; import java.util.List; +import java.util.Locale; import java.util.Set; +import java.util.TimeZone; import java.util.logging.Level; import javax.swing.JOptionPane; import javax.swing.SwingUtilities; @@ -282,7 +284,7 @@ public final class GeolocationTopComponent extends TopComponent { Case currentCase = Case.getCurrentCase(); // Create the root reports directory path of the form: /Reports/ / - DateFormat dateFormat = new SimpleDateFormat("MM-dd-yyyy-HH-mm-ss"); + DateFormat dateFormat = new SimpleDateFormat("MM-dd-yyyy-HH-mm-ss", Locale.US); Date date = new Date(); String dateNoTime = dateFormat.format(date); String reportPath = String.format(REPORT_PATH_FMT_STR, currentCase.getReportDirectory(), currentCase.getDisplayName(), "Goggle Earth KML", dateNoTime); From 3cafd4e6319643c511b4b0f295dafe121f394728 Mon Sep 17 00:00:00 2001 From: Kelly Kelly Date: Thu, 5 Dec 2019 09:52:59 -0500 Subject: [PATCH 3/4] Added support for waypoint selection --- .../autopsy/geolocation/MapPanel.java | 55 ++++++++++++++++++ .../autopsy/images/waypoint_teal.png | Bin 0 -> 912 bytes .../autopsy/images/waypoint_yellow.png | Bin 0 -> 891 bytes 3 files changed, 55 insertions(+) create mode 100755 Core/src/org/sleuthkit/autopsy/images/waypoint_teal.png create mode 100755 Core/src/org/sleuthkit/autopsy/images/waypoint_yellow.png diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/MapPanel.java b/Core/src/org/sleuthkit/autopsy/geolocation/MapPanel.java index fa74e2b214..79ac8333ff 100755 --- a/Core/src/org/sleuthkit/autopsy/geolocation/MapPanel.java +++ b/Core/src/org/sleuthkit/autopsy/geolocation/MapPanel.java @@ -19,6 +19,7 @@ package org.sleuthkit.autopsy.geolocation; import java.awt.Dimension; +import java.awt.Graphics2D; import java.awt.Point; import java.awt.Rectangle; import java.awt.event.ActionEvent; @@ -27,9 +28,12 @@ import java.awt.event.ComponentAdapter; import java.awt.event.ComponentEvent; import java.awt.event.MouseEvent; import java.awt.geom.Point2D; +import java.awt.image.BufferedImage; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.io.File; +import java.io.IOException; +import java.net.URL; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; @@ -47,6 +51,7 @@ import javax.swing.Popup; import javax.swing.PopupFactory; import javax.swing.Timer; import javax.swing.event.MouseInputListener; +import org.jxmapviewer.JXMapViewer; import org.jxmapviewer.OSMTileFactoryInfo; import org.jxmapviewer.VirtualEarthTileFactoryInfo; import org.jxmapviewer.input.CenterMapListener; @@ -58,6 +63,7 @@ import org.jxmapviewer.viewer.TileFactory; import org.jxmapviewer.viewer.TileFactoryInfo; import org.jxmapviewer.viewer.Waypoint; import org.jxmapviewer.viewer.WaypointPainter; +import org.jxmapviewer.viewer.WaypointRenderer; import org.jxmapviewer.viewer.util.GeoUtil; import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.core.UserPreferences; @@ -65,6 +71,9 @@ import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; import org.sleuthkit.autopsy.geolocation.datamodel.GeoLocationDataException; import org.sleuthkit.datamodel.TskCoreException; +import javax.imageio.ImageIO; +import org.jxmapviewer.viewer.DefaultWaypointRenderer; +import org.openide.util.Exceptions; /** * The map panel. This panel contains the jxmapviewer MapViewer @@ -132,6 +141,11 @@ final public class MapPanel extends javax.swing.JPanel { }); } + /** + * Get a list of the waypoints that are currently visible in the viewport. + * + * @return A list of waypoints or empty list if none were found. + */ List getVisibleWaypoints() { Rectangle viewport = mapViewer.getViewportBounds(); @@ -194,6 +208,13 @@ final public class MapPanel extends javax.swing.JPanel { return set; } }; + + try { + waypointPainter.setRenderer(new MapWaypointRenderer()); + } catch (IOException ex) { + logger.log(Level.WARNING, "Failed to load waypoint image resource, using DefaultWaypointRenderer", ex); + waypointPainter.setRenderer(new DefaultWaypointRenderer()); + } mapViewer.setOverlayPainter(waypointPainter); } @@ -322,6 +343,7 @@ final public class MapPanel extends javax.swing.JPanel { if(currentPopup != null) { showDetailsPopup(); } + mapViewer.repaint(); } } catch (TskCoreException ex) { logger.log(Level.WARNING, "Failed to show popup for waypoint", ex); @@ -379,6 +401,8 @@ final public class MapPanel extends javax.swing.JPanel { currentPopup = popupFactory.getPopup(this, detailPane, popupLocation.x, popupLocation.y); currentPopup.show(); + + mapViewer.repaint(); } } @@ -609,4 +633,35 @@ final public class MapPanel extends javax.swing.JPanel { private javax.swing.JPanel zoomPanel; private javax.swing.JSlider zoomSlider; // End of variables declaration//GEN-END:variables + + /** + * Renderer for the map waypoints. + */ + private class MapWaypointRenderer implements WaypointRenderer { + private final BufferedImage defaultWaypointImage; + private final BufferedImage selectedWaypointImage; + + /** + * Construct a WaypointRenederer + * + * @throws IOException + */ + MapWaypointRenderer() throws IOException { + defaultWaypointImage = ImageIO.read(getClass().getResource("/org/sleuthkit/autopsy/images/waypoint_teal.png")); + selectedWaypointImage = ImageIO.read(getClass().getResource("/org/sleuthkit/autopsy/images/waypoint_yellow.png")); + } + + @Override + public void paintWaypoint(Graphics2D gd, JXMapViewer jxmv, Waypoint waypoint) { + Point2D point = jxmv.getTileFactory().geoToPixel(waypoint.getPosition(), jxmv.getZoom()); + + int x = (int)point.getX(); + int y = (int)point.getY(); + + BufferedImage image = (waypoint == currentlySelectedWaypoint ? selectedWaypointImage: defaultWaypointImage); + + (gd.create()).drawImage(image, x -image.getWidth() / 2, y -image.getHeight(), null); + } + + } } diff --git a/Core/src/org/sleuthkit/autopsy/images/waypoint_teal.png b/Core/src/org/sleuthkit/autopsy/images/waypoint_teal.png new file mode 100755 index 0000000000000000000000000000000000000000..3860d1973cc4d86c5a8392953b1699753325a4ea GIT binary patch literal 912 zcmV;B18@9^P)254giDped|VH>fE=Ap7n7czPD)BjKsGfs#qjVj^Yimd zz)heKm=0z97q|`hAvrlYzPh@aEAQlot$$DN4Vs&qnVg(l2txZ5VJL7r;I`ZC8h=OY zGAHqDY5j+F^!D~X0p13#h!q8x2u$YX<;DH;#fcTc6w%8c9igYE=P|HBoQeYU==J(H z(tdKU3Z{ssZ3T>vkKY2a1yFQxu(h>S<1DmC1>AS-7b+?$#MW&Bcsf2lzB23XKvXa# zX(ZFi+}vEJ0Nyg2&6YKn}xN?%xAIS(Z1> zocc@^&`{SzS65d6_^ZWY*{pSar3&~$X(e8-cSr!w=ybY+nImG7qK{xx&I_Pf%w>Cf zyXL}Mxls}Kdj=^hD--i=S1z_!uh-{Bd$AwBDKj=UHVAA{fJwj~vMg&?+niu`7lBQ{ zSg1Md1imXOD$@MfRJWq7n~t{Oa5%(->;asi=AZzIfv(2JM#)_&tS)lw{l-I8Rh1A_ z0`1}KOZj#ZIMmoMVgi@r;ZNL&EnL!vYiDKTKa= z-y9%p@41Ks%mI9DZEez}UBY*tMg7PG`S}4Se-AjjQlQY?G#Cuo$=+|D%UOHiW87}{ zBjA;Au|%$Y0!D&cMLHkAO8=pb@y|@pz>A4KFchG_$a}?+is^r`2jLNlHq>>2wAh zMJ@2t)YQ~tTCJAR(NXb|m8k_5$HvBXOOhlSQUiRh7ARi-t3k-S!CyI5XJ`WM1PMEU m>uM`p2h0F5fB~3T4)_%M&iOpzp8Wj)00001BCpEP)D76G53-ZFC4=*%RFT#o} zEdoUsq0We@1mQYk=Kbs^q0}HP8kmX|>vAF)=ZOg@w7pCMG5r85zOla?Jt*fCczC*Z3Dq0lGp% zLxW38ODQWm?sfikb(g5Bs>11X&UnyHC2%gd6|kAj=764_`#yH!@6gVjGFGegH?SM% z5F-kx1DxsU=|SDywnbq|kj1i#_V)IlzmxTJ4eV-(9Le&!OBDV7z#j4Gj$ehYug|3vFt;PF`M~c;9B_V)yFx z`c!`}c4TA(q{%7mJu8put=DQu;0eNBjEvNJ&ZcijI!H z!e}%yJw1H~*z2pnDr1h=*jU5!=P$g1J9ccRv$Jyw5M^&(^Y#f40ZBlAZEdYoQgU=& zLU(r;nVFfQ?+|c#v7m?>Fc=I84#&HBIT;!0*le~>z#4DUUucFRHW7H#*48FjETY20 z>(`@1M@KU|J9`Q^w^UHXzmsKoopLwNoT;RuqT&Y-w9pmsHA9heUZ>NYn4J9T&QDBC zU~q8oA&~4Z7$sI}x7($(G!x60YnYjt5g-3Xe?gJ=t-88;Wn5ewCX-3LinYLJHQ Date: Thu, 5 Dec 2019 11:49:10 -0500 Subject: [PATCH 4/4] Removed unused imports --- .../sleuthkit/autopsy/geolocation/GeolocationTopComponent.java | 1 - Core/src/org/sleuthkit/autopsy/geolocation/MapPanel.java | 2 -- 2 files changed, 3 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/GeolocationTopComponent.java b/Core/src/org/sleuthkit/autopsy/geolocation/GeolocationTopComponent.java index d810664c5a..94bf231553 100755 --- a/Core/src/org/sleuthkit/autopsy/geolocation/GeolocationTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/geolocation/GeolocationTopComponent.java @@ -33,7 +33,6 @@ import java.util.EnumSet; import java.util.List; import java.util.Locale; import java.util.Set; -import java.util.TimeZone; import java.util.logging.Level; import javax.swing.JOptionPane; import javax.swing.SwingUtilities; diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/MapPanel.java b/Core/src/org/sleuthkit/autopsy/geolocation/MapPanel.java index 79ac8333ff..3007a8021a 100755 --- a/Core/src/org/sleuthkit/autopsy/geolocation/MapPanel.java +++ b/Core/src/org/sleuthkit/autopsy/geolocation/MapPanel.java @@ -33,7 +33,6 @@ import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.io.File; import java.io.IOException; -import java.net.URL; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; @@ -73,7 +72,6 @@ import org.sleuthkit.autopsy.geolocation.datamodel.GeoLocationDataException; import org.sleuthkit.datamodel.TskCoreException; import javax.imageio.ImageIO; import org.jxmapviewer.viewer.DefaultWaypointRenderer; -import org.openide.util.Exceptions; /** * The map panel. This panel contains the jxmapviewer MapViewer