diff --git a/Core/src/org/sleuthkit/autopsy/core/UserPreferences.java b/Core/src/org/sleuthkit/autopsy/core/UserPreferences.java index b65d0873fe..409e27f080 100644 --- a/Core/src/org/sleuthkit/autopsy/core/UserPreferences.java +++ b/Core/src/org/sleuthkit/autopsy/core/UserPreferences.java @@ -81,6 +81,7 @@ public final class UserPreferences { private static final String GEO_TILE_OPTION = "GeolocationTileOption"; private static final String GEO_OSM_TILE_ZIP_PATH = "GeolocationOsmZipPath"; private static final String GEO_OSM_SERVER_ADDRESS = "GeolocationOsmServerAddress"; + private static final String GEO_MBTILES_FILE_PATH = "GeolcoationMBTilesFilePath"; // Prevent instantiation. private UserPreferences() { @@ -576,7 +577,7 @@ public final class UserPreferences { } /** - * Sets the address of the OSM tile server. + * Sets the address of geolocation window user defined OSM server data source. * * @param address */ @@ -592,4 +593,22 @@ public final class UserPreferences { public static String getGeolocationOsmServerAddress() { return preferences.get(GEO_OSM_SERVER_ADDRESS, ""); } + + /** + * Sets the path for Geolocation MBTiles data source file. + * + * @param absolutePath + */ + public static void setGeolocationMBTilesFilePath(String absolutePath) { + preferences.put(GEO_MBTILES_FILE_PATH, absolutePath); + } + + /** + * Retrieves the path for the Geolocation MBTiles data source file. + * + * @return Absolute path to MBTiles file or empty string if none was found. + */ + public static String getGeolocationMBTilesFilePath() { + return preferences.get(GEO_MBTILES_FILE_PATH, ""); + } } diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/Bundle.properties b/Core/src/org/sleuthkit/autopsy/geolocation/Bundle.properties index 84d4ab700c..5f7678fa4e 100755 --- a/Core/src/org/sleuthkit/autopsy/geolocation/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/geolocation/Bundle.properties @@ -19,14 +19,18 @@ CheckBoxListPanel.uncheckButton.text=Uncheck All GeoFilterPanel.optionsLabel.text=Waypoints OptionsCategory_Name_Geolocation=Geolocation OptionsCategory_Keywords_Geolocation=Geolocation Settings -GeolocationSettingsPanel.tilePane.border.title=Map Tile Source +GeolocationSettingsPanel.tilePane.border.title=Map Tile Data Source OptionsCategory_Keywords_Geolocation=Geolocation OptionsCategory_Name_Geolocation=Geolocation -GeolocationSettingsPanel.defaultButton.text=Default online tile server (bing.com/maps) -GeolocationSettingsPanel.tileServerButton.text=OpenStreetMap tile server -GeolocationSettingsPanel.tileServerFiled.text= -GeolocationSettingsPanel.osmZipButton.text=OpenStreeMap Tile Zip File -GeolocationSettingsPanel.osmZipFileField.text= -GeolocationSettingsPanel.osmZipFileBrowseButton.text=Browse -GeolocationSettingsPanel.serverTestButton.text=Test -GeolocationSettingsPanel.osmZipButton.actionCommand=OpenStreeMap tile ZIP file +GeolocationSettingsPanel.mbtileFileField.toolTipText= +GeolocationSettingsPanel.mbtileFileField.text= +GeolocationSettingsPanel.defaultDataSource.text=Default online tile server (bing.com/maps) +GeolocationSettingsPanel.osmServerRBnt.text=OpenStreetMap server +GeolocationSettingsPanel.zipFileRBnt.text=OpenStreeMap zip file +GeolocationSettingsPanel.zipFileRBnt.actionCommand=OpenStreeMap tile ZIP file +GeolocationSettingsPanel.mbtilesRBtn.text=MBTiles file +GeolocationSettingsPanel.osmServerAddressField.text= +GeolocationSettingsPanel.zipFilePathField.text= +GeolocationSettingsPanel.serverTestBtn.text=Test +GeolocationSettingsPanel.mbtilesBrowseBtn.text=Browse +GeolocationSettingsPanel.zipFileBrowseBnt.text=Browse diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/geolocation/Bundle.properties-MERGED index c69264acae..528a2ff9cf 100755 --- a/Core/src/org/sleuthkit/autopsy/geolocation/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/geolocation/Bundle.properties-MERGED @@ -43,15 +43,19 @@ CheckBoxListPanel.uncheckButton.text=Uncheck All GeoFilterPanel.optionsLabel.text=Waypoints OptionsCategory_Name_Geolocation=Geolocation OptionsCategory_Keywords_Geolocation=Geolocation Settings -GeolocationSettingsPanel.tilePane.border.title=Map Tile Source +GeolocationSettingsPanel.tilePane.border.title=Map Tile Data Source OptionsCategory_Keywords_Geolocation=Geolocation OptionsCategory_Name_Geolocation=Geolocation -GeolocationSettingsPanel.defaultButton.text=Default online tile server (bing.com/maps) -GeolocationSettingsPanel.tileServerButton.text=OpenStreetMap tile server -GeolocationSettingsPanel.tileServerFiled.text= -GeolocationSettingsPanel.osmZipButton.text=OpenStreeMap Tile Zip File -GeolocationSettingsPanel.osmZipFileField.text= -GeolocationSettingsPanel.osmZipFileBrowseButton.text=Browse -GeolocationSettingsPanel.serverTestButton.text=Test -GeolocationSettingsPanel.osmZipButton.actionCommand=OpenStreeMap tile ZIP file +GeolocationSettingsPanel.mbtileFileField.toolTipText= +GeolocationSettingsPanel.mbtileFileField.text= +GeolocationSettingsPanel.defaultDataSource.text=Default online tile server (bing.com/maps) +GeolocationSettingsPanel.osmServerRBnt.text=OpenStreetMap server +GeolocationSettingsPanel.zipFileRBnt.text=OpenStreeMap zip file +GeolocationSettingsPanel.zipFileRBnt.actionCommand=OpenStreeMap tile ZIP file +GeolocationSettingsPanel.mbtilesRBtn.text=MBTiles file +GeolocationSettingsPanel.osmServerAddressField.text= +GeolocationSettingsPanel.zipFilePathField.text= +GeolocationSettingsPanel.serverTestBtn.text=Test +GeolocationSettingsPanel.mbtilesBrowseBtn.text=Browse +GeolocationSettingsPanel.zipFileBrowseBnt.text=Browse WaypointExtractAction_label=Extract Files(s) diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/GeolocationSettingsPanel.form b/Core/src/org/sleuthkit/autopsy/geolocation/GeolocationSettingsPanel.form index 0fd5461656..421eb626df 100755 --- a/Core/src/org/sleuthkit/autopsy/geolocation/GeolocationSettingsPanel.form +++ b/Core/src/org/sleuthkit/autopsy/geolocation/GeolocationSettingsPanel.form @@ -28,7 +28,7 @@ - + @@ -46,72 +46,36 @@ - + - + - + - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -119,10 +83,46 @@ - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -134,14 +134,14 @@ - + - + - + @@ -149,18 +149,69 @@ - + - + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/GeolocationSettingsPanel.java b/Core/src/org/sleuthkit/autopsy/geolocation/GeolocationSettingsPanel.java index 9be1f08927..a43917791b 100755 --- a/Core/src/org/sleuthkit/autopsy/geolocation/GeolocationSettingsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/geolocation/GeolocationSettingsPanel.java @@ -52,24 +52,29 @@ final class GeolocationSettingsPanel extends javax.swing.JPanel implements Optio @Override public void store() { - UserPreferences.setGeolocationTileOption(getServerOption().getValue()); - UserPreferences.setGeolocationOsmZipPath(osmZipFileField.getText()); - UserPreferences.setGeolocationOsmServerAddress(tileServerFiled.getText()); + UserPreferences.setGeolocationTileOption(getDataSourceOption().getValue()); + UserPreferences.setGeolocationOsmZipPath(zipFilePathField.getText()); + UserPreferences.setGeolocationOsmServerAddress(osmServerAddressField.getText()); + UserPreferences.setGeolocationMBTilesFilePath(mbtileFileField.getText()); } @Override public void load() { - tileServerFiled.setText(UserPreferences.getGeolocationOsmServerAddress()); - osmZipFileField.setText(UserPreferences.getGeolocationOsmZipPath()); - switch (GeolocationTileOption.getOptionForValue(UserPreferences.getGeolocationtTileOption())) { + osmServerAddressField.setText(UserPreferences.getGeolocationOsmServerAddress()); + zipFilePathField.setText(UserPreferences.getGeolocationOsmZipPath()); + mbtileFileField.setText(UserPreferences.getGeolocationMBTilesFilePath()); + switch (GeolocationDataSourceType.getOptionForValue(UserPreferences.getGeolocationtTileOption())) { case ONLINE_USER_DEFINED_OSM_SERVER: - tileServerButton.setSelected(true); + osmServerRBnt.setSelected(true); break; case OFFLINE_OSM_ZIP: - osmZipButton.setSelected(true); + zipFileRBnt.setSelected(true); + break; + case OFFILE_MBTILES_FILE: + mbtilesRBtn.setSelected(true); break; default: - defaultButton.setSelected(true); + defaultDataSource.setSelected(true); break; } @@ -81,10 +86,12 @@ final class GeolocationSettingsPanel extends javax.swing.JPanel implements Optio * selection state. */ private void updateControlState() { - tileServerFiled.setEnabled(tileServerButton.isSelected()); - serverTestButton.setEnabled(tileServerButton.isSelected()); - osmZipFileField.setEnabled(osmZipButton.isSelected()); - osmZipFileBrowseButton.setEnabled(osmZipButton.isSelected()); + osmServerAddressField.setEnabled(osmServerRBnt.isSelected()); + serverTestBtn.setEnabled(osmServerRBnt.isSelected()); + zipFilePathField.setEnabled(zipFileRBnt.isSelected()); + zipFileBrowseBnt.setEnabled(zipFileRBnt.isSelected()); + mbtileFileField.setEnabled(mbtilesRBtn.isSelected()); + mbtilesBrowseBtn.setEnabled(mbtilesRBtn.isSelected()); } /** @@ -93,13 +100,15 @@ final class GeolocationSettingsPanel extends javax.swing.JPanel implements Optio * * @return Current GEOLOCATION_TILE_OPTION */ - private GeolocationTileOption getServerOption() { - if (tileServerButton.isSelected()) { - return GeolocationTileOption.ONLINE_USER_DEFINED_OSM_SERVER; - } else if (osmZipButton.isSelected()) { - return GeolocationTileOption.OFFLINE_OSM_ZIP; + private GeolocationDataSourceType getDataSourceOption() { + if (osmServerRBnt.isSelected()) { + return GeolocationDataSourceType.ONLINE_USER_DEFINED_OSM_SERVER; + } else if (zipFileRBnt.isSelected()) { + return GeolocationDataSourceType.OFFLINE_OSM_ZIP; + } else if (mbtilesRBtn.isSelected()) { + return GeolocationDataSourceType.OFFILE_MBTILES_FILE; } - return GeolocationTileOption.ONLINE_DEFAULT_SERVER; + return GeolocationDataSourceType.ONLINE_DEFAULT_SERVER; } /** @@ -135,25 +144,28 @@ final class GeolocationSettingsPanel extends javax.swing.JPanel implements Optio javax.swing.ButtonGroup buttonGroup = new javax.swing.ButtonGroup(); javax.swing.JPanel tilePane = new javax.swing.JPanel(); - defaultButton = new javax.swing.JRadioButton(); - tileServerButton = new javax.swing.JRadioButton(); - tileServerFiled = new javax.swing.JTextField(); - osmZipButton = new javax.swing.JRadioButton(); - osmZipFileField = new javax.swing.JTextField(); - osmZipFileBrowseButton = new javax.swing.JButton(); - serverTestButton = new javax.swing.JButton(); + defaultDataSource = new javax.swing.JRadioButton(); + osmServerRBnt = new javax.swing.JRadioButton(); + osmServerAddressField = new javax.swing.JTextField(); + zipFileRBnt = new javax.swing.JRadioButton(); + zipFilePathField = new javax.swing.JTextField(); + zipFileBrowseBnt = new javax.swing.JButton(); + serverTestBtn = new javax.swing.JButton(); + mbtilesRBtn = new javax.swing.JRadioButton(); + mbtileFileField = new javax.swing.JTextField(); + mbtilesBrowseBtn = new javax.swing.JButton(); setLayout(new java.awt.GridBagLayout()); tilePane.setBorder(javax.swing.BorderFactory.createTitledBorder(org.openide.util.NbBundle.getMessage(GeolocationSettingsPanel.class, "GeolocationSettingsPanel.tilePane.border.title"))); // NOI18N tilePane.setLayout(new java.awt.GridBagLayout()); - buttonGroup.add(defaultButton); - defaultButton.setSelected(true); - org.openide.awt.Mnemonics.setLocalizedText(defaultButton, org.openide.util.NbBundle.getMessage(GeolocationSettingsPanel.class, "GeolocationSettingsPanel.defaultButton.text")); // NOI18N - defaultButton.addActionListener(new java.awt.event.ActionListener() { + buttonGroup.add(defaultDataSource); + defaultDataSource.setSelected(true); + org.openide.awt.Mnemonics.setLocalizedText(defaultDataSource, org.openide.util.NbBundle.getMessage(GeolocationSettingsPanel.class, "GeolocationSettingsPanel.defaultDataSource.text")); // NOI18N + defaultDataSource.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { - defaultButtonActionPerformed(evt); + defaultDataSourceActionPerformed(evt); } }); gridBagConstraints = new java.awt.GridBagConstraints(); @@ -162,56 +174,56 @@ final class GeolocationSettingsPanel extends javax.swing.JPanel implements Optio gridBagConstraints.gridwidth = 3; gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; gridBagConstraints.weightx = 1.0; - gridBagConstraints.insets = new java.awt.Insets(0, 0, 5, 0); - tilePane.add(defaultButton, gridBagConstraints); + gridBagConstraints.insets = new java.awt.Insets(9, 0, 9, 0); + tilePane.add(defaultDataSource, gridBagConstraints); - buttonGroup.add(tileServerButton); - org.openide.awt.Mnemonics.setLocalizedText(tileServerButton, org.openide.util.NbBundle.getMessage(GeolocationSettingsPanel.class, "GeolocationSettingsPanel.tileServerButton.text")); // NOI18N - tileServerButton.addActionListener(new java.awt.event.ActionListener() { + buttonGroup.add(osmServerRBnt); + org.openide.awt.Mnemonics.setLocalizedText(osmServerRBnt, org.openide.util.NbBundle.getMessage(GeolocationSettingsPanel.class, "GeolocationSettingsPanel.osmServerRBnt.text")); // NOI18N + osmServerRBnt.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { - tileServerButtonActionPerformed(evt); - } - }); - gridBagConstraints = new java.awt.GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; - gridBagConstraints.insets = new java.awt.Insets(0, 0, 5, 0); - tilePane.add(tileServerButton, gridBagConstraints); - - tileServerFiled.setText(org.openide.util.NbBundle.getMessage(GeolocationSettingsPanel.class, "GeolocationSettingsPanel.tileServerFiled.text")); // NOI18N - tileServerFiled.setPreferredSize(new java.awt.Dimension(300, 26)); - gridBagConstraints = new java.awt.GridBagConstraints(); - gridBagConstraints.gridx = 1; - gridBagConstraints.gridy = 1; - gridBagConstraints.insets = new java.awt.Insets(0, 0, 5, 0); - tilePane.add(tileServerFiled, gridBagConstraints); - - buttonGroup.add(osmZipButton); - org.openide.awt.Mnemonics.setLocalizedText(osmZipButton, org.openide.util.NbBundle.getMessage(GeolocationSettingsPanel.class, "GeolocationSettingsPanel.osmZipButton.text")); // NOI18N - osmZipButton.setActionCommand(org.openide.util.NbBundle.getMessage(GeolocationSettingsPanel.class, "GeolocationSettingsPanel.osmZipButton.actionCommand")); // NOI18N - osmZipButton.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - osmZipButtonActionPerformed(evt); + osmServerRBntActionPerformed(evt); } }); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 0; gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; gridBagConstraints.insets = new java.awt.Insets(0, 0, 9, 0); - tilePane.add(osmZipButton, gridBagConstraints); + tilePane.add(osmServerRBnt, gridBagConstraints); - osmZipFileField.setText(org.openide.util.NbBundle.getMessage(GeolocationSettingsPanel.class, "GeolocationSettingsPanel.osmZipFileField.text")); // NOI18N - osmZipFileField.setPreferredSize(new java.awt.Dimension(300, 26)); + osmServerAddressField.setText(org.openide.util.NbBundle.getMessage(GeolocationSettingsPanel.class, "GeolocationSettingsPanel.osmServerAddressField.text")); // NOI18N + osmServerAddressField.setPreferredSize(new java.awt.Dimension(300, 26)); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 1; + gridBagConstraints.gridy = 1; + gridBagConstraints.insets = new java.awt.Insets(0, 0, 9, 0); + tilePane.add(osmServerAddressField, gridBagConstraints); + + buttonGroup.add(zipFileRBnt); + org.openide.awt.Mnemonics.setLocalizedText(zipFileRBnt, org.openide.util.NbBundle.getMessage(GeolocationSettingsPanel.class, "GeolocationSettingsPanel.zipFileRBnt.text")); // NOI18N + zipFileRBnt.setActionCommand(org.openide.util.NbBundle.getMessage(GeolocationSettingsPanel.class, "GeolocationSettingsPanel.zipFileRBnt.actionCommand")); // NOI18N + zipFileRBnt.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + zipFileRBntActionPerformed(evt); + } + }); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; + gridBagConstraints.insets = new java.awt.Insets(0, 0, 9, 0); + tilePane.add(zipFileRBnt, gridBagConstraints); + + zipFilePathField.setText(org.openide.util.NbBundle.getMessage(GeolocationSettingsPanel.class, "GeolocationSettingsPanel.zipFilePathField.text")); // NOI18N + zipFilePathField.setPreferredSize(new java.awt.Dimension(300, 26)); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 1; gridBagConstraints.gridy = 2; gridBagConstraints.insets = new java.awt.Insets(0, 0, 9, 0); - tilePane.add(osmZipFileField, gridBagConstraints); + tilePane.add(zipFilePathField, gridBagConstraints); - org.openide.awt.Mnemonics.setLocalizedText(osmZipFileBrowseButton, org.openide.util.NbBundle.getMessage(GeolocationSettingsPanel.class, "GeolocationSettingsPanel.osmZipFileBrowseButton.text")); // NOI18N - osmZipFileBrowseButton.addActionListener(new java.awt.event.ActionListener() { + org.openide.awt.Mnemonics.setLocalizedText(zipFileBrowseBnt, org.openide.util.NbBundle.getMessage(GeolocationSettingsPanel.class, "GeolocationSettingsPanel.zipFileBrowseBnt.text")); // NOI18N + zipFileBrowseBnt.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { - osmZipFileBrowseButtonActionPerformed(evt); + zipFileBrowseBntActionPerformed(evt); } }); gridBagConstraints = new java.awt.GridBagConstraints(); @@ -220,20 +232,57 @@ final class GeolocationSettingsPanel extends javax.swing.JPanel implements Optio gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; gridBagConstraints.weightx = 1.0; gridBagConstraints.insets = new java.awt.Insets(0, 9, 9, 9); - tilePane.add(osmZipFileBrowseButton, gridBagConstraints); + tilePane.add(zipFileBrowseBnt, gridBagConstraints); - org.openide.awt.Mnemonics.setLocalizedText(serverTestButton, org.openide.util.NbBundle.getMessage(GeolocationSettingsPanel.class, "GeolocationSettingsPanel.serverTestButton.text")); // NOI18N - serverTestButton.addActionListener(new java.awt.event.ActionListener() { + org.openide.awt.Mnemonics.setLocalizedText(serverTestBtn, org.openide.util.NbBundle.getMessage(GeolocationSettingsPanel.class, "GeolocationSettingsPanel.serverTestBtn.text")); // NOI18N + serverTestBtn.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { - serverTestButtonActionPerformed(evt); + serverTestBtnActionPerformed(evt); } }); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 2; gridBagConstraints.gridy = 1; + gridBagConstraints.ipadx = 20; gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; gridBagConstraints.insets = new java.awt.Insets(0, 9, 9, 9); - tilePane.add(serverTestButton, gridBagConstraints); + tilePane.add(serverTestBtn, gridBagConstraints); + + buttonGroup.add(mbtilesRBtn); + org.openide.awt.Mnemonics.setLocalizedText(mbtilesRBtn, org.openide.util.NbBundle.getMessage(GeolocationSettingsPanel.class, "GeolocationSettingsPanel.mbtilesRBtn.text")); // NOI18N + mbtilesRBtn.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + mbtilesRBtnActionPerformed(evt); + } + }); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 3; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; + gridBagConstraints.insets = new java.awt.Insets(0, 0, 9, 0); + tilePane.add(mbtilesRBtn, gridBagConstraints); + + mbtileFileField.setText(org.openide.util.NbBundle.getMessage(GeolocationSettingsPanel.class, "GeolocationSettingsPanel.mbtileFileField.text")); // NOI18N + mbtileFileField.setToolTipText(org.openide.util.NbBundle.getMessage(GeolocationSettingsPanel.class, "GeolocationSettingsPanel.mbtileFileField.toolTipText")); // NOI18N + mbtileFileField.setPreferredSize(new java.awt.Dimension(300, 26)); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 1; + gridBagConstraints.gridy = 3; + gridBagConstraints.insets = new java.awt.Insets(0, 0, 9, 0); + tilePane.add(mbtileFileField, gridBagConstraints); + + org.openide.awt.Mnemonics.setLocalizedText(mbtilesBrowseBtn, org.openide.util.NbBundle.getMessage(GeolocationSettingsPanel.class, "GeolocationSettingsPanel.mbtilesBrowseBtn.text")); // NOI18N + mbtilesBrowseBtn.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + mbtilesBrowseBtnActionPerformed(evt); + } + }); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 2; + gridBagConstraints.gridy = 3; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; + gridBagConstraints.insets = new java.awt.Insets(0, 9, 9, 9); + tilePane.add(mbtilesBrowseBtn, gridBagConstraints); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; @@ -243,7 +292,7 @@ final class GeolocationSettingsPanel extends javax.swing.JPanel implements Optio add(tilePane, gridBagConstraints); }// //GEN-END:initComponents - private void osmZipFileBrowseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_osmZipFileBrowseButtonActionPerformed + private void zipFileBrowseBntActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_zipFileBrowseBntActionPerformed JFileChooser fileWindow = new JFileChooser(); fileWindow.setFileSelectionMode(JFileChooser.FILES_ONLY); GeneralFilter fileFilter = new GeneralFilter(Arrays.asList(".zip"), "Zips (*.zip)"); //NON-NLS @@ -253,26 +302,26 @@ final class GeolocationSettingsPanel extends javax.swing.JPanel implements Optio int returnVal = fileWindow.showSaveDialog(this); if (returnVal == JFileChooser.APPROVE_OPTION) { File zipFile = fileWindow.getSelectedFile(); - osmZipFileField.setForeground(Color.BLACK); - osmZipFileField.setText(zipFile.getAbsolutePath()); + zipFilePathField.setForeground(Color.BLACK); + zipFilePathField.setText(zipFile.getAbsolutePath()); firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null); } - }//GEN-LAST:event_osmZipFileBrowseButtonActionPerformed + }//GEN-LAST:event_zipFileBrowseBntActionPerformed - private void defaultButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_defaultButtonActionPerformed + private void defaultDataSourceActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_defaultDataSourceActionPerformed updateControlState(); firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null); - }//GEN-LAST:event_defaultButtonActionPerformed + }//GEN-LAST:event_defaultDataSourceActionPerformed - private void tileServerButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_tileServerButtonActionPerformed + private void osmServerRBntActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_osmServerRBntActionPerformed updateControlState(); firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null); - }//GEN-LAST:event_tileServerButtonActionPerformed + }//GEN-LAST:event_osmServerRBntActionPerformed - private void osmZipButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_osmZipButtonActionPerformed + private void zipFileRBntActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_zipFileRBntActionPerformed updateControlState(); firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null); - }//GEN-LAST:event_osmZipButtonActionPerformed + }//GEN-LAST:event_zipFileRBntActionPerformed @Messages({ "GeolocationSettingsPanel_malformed_url_message=The supplies OSM tile server address is invalid.\nPlease supply a well formed url prefixed with http://", @@ -281,8 +330,8 @@ final class GeolocationSettingsPanel extends javax.swing.JPanel implements Optio "GeolocationSettingsPanel_osm_server_test_fail_message_title=Error", "GeolocationSettingsPanel_osm_server_test_success_message=The provide OSM tile server address is valid.", "GeolocationSettingsPanel_osm_server_test_success_message_title=Success",}) - private void serverTestButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_serverTestButtonActionPerformed - String address = tileServerFiled.getText(); + private void serverTestBtnActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_serverTestBtnActionPerformed + String address = osmServerAddressField.getText(); String message = Bundle.GeolocationSettingsPanel_osm_server_test_fail_message(); String title = Bundle.GeolocationSettingsPanel_osm_server_test_fail_message_title(); @@ -297,31 +346,56 @@ final class GeolocationSettingsPanel extends javax.swing.JPanel implements Optio } JOptionPane.showMessageDialog(this, message, title, JOptionPane.INFORMATION_MESSAGE); - }//GEN-LAST:event_serverTestButtonActionPerformed + }//GEN-LAST:event_serverTestBtnActionPerformed + + private void mbtilesRBtnActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_mbtilesRBtnActionPerformed + updateControlState(); + firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null); + }//GEN-LAST:event_mbtilesRBtnActionPerformed + + private void mbtilesBrowseBtnActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_mbtilesBrowseBtnActionPerformed + JFileChooser fileWindow = new JFileChooser(); + fileWindow.setFileSelectionMode(JFileChooser.FILES_ONLY); + GeneralFilter fileFilter = new GeneralFilter(Arrays.asList(".mbtiles"), "MBTiles (*.mbtiles)"); //NON-NLS + fileWindow.setDragEnabled(false); + fileWindow.setFileFilter(fileFilter); + fileWindow.setMultiSelectionEnabled(false); + int returnVal = fileWindow.showSaveDialog(this); + if (returnVal == JFileChooser.APPROVE_OPTION) { + File zipFile = fileWindow.getSelectedFile(); + mbtileFileField.setForeground(Color.BLACK); + mbtileFileField.setText(zipFile.getAbsolutePath()); + firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null); + } + }//GEN-LAST:event_mbtilesBrowseBtnActionPerformed // Variables declaration - do not modify//GEN-BEGIN:variables - private javax.swing.JRadioButton defaultButton; - private javax.swing.JRadioButton osmZipButton; - private javax.swing.JButton osmZipFileBrowseButton; - private javax.swing.JTextField osmZipFileField; - private javax.swing.JButton serverTestButton; - private javax.swing.JRadioButton tileServerButton; - private javax.swing.JTextField tileServerFiled; + private javax.swing.JRadioButton defaultDataSource; + private javax.swing.JTextField mbtileFileField; + private javax.swing.JButton mbtilesBrowseBtn; + private javax.swing.JRadioButton mbtilesRBtn; + private javax.swing.JTextField osmServerAddressField; + private javax.swing.JRadioButton osmServerRBnt; + private javax.swing.JButton serverTestBtn; + private javax.swing.JButton zipFileBrowseBnt; + private javax.swing.JTextField zipFilePathField; + private javax.swing.JRadioButton zipFileRBnt; // End of variables declaration//GEN-END:variables /** * Tile server option enum. The enum was given values to simplify the * storing of the user preference for a particular option. */ - enum GeolocationTileOption{ + enum GeolocationDataSourceType{ ONLINE_DEFAULT_SERVER(0), ONLINE_USER_DEFINED_OSM_SERVER(1), - OFFLINE_OSM_ZIP(2); + OFFLINE_OSM_ZIP(2), + OFFILE_MBTILES_FILE(3); private final int value; - GeolocationTileOption(int value) { + GeolocationDataSourceType(int value) { this.value = value; } @@ -329,8 +403,8 @@ final class GeolocationSettingsPanel extends javax.swing.JPanel implements Optio return value; } - static GeolocationTileOption getOptionForValue(int value) { - for (GeolocationTileOption option : GeolocationTileOption.values()) { + static GeolocationDataSourceType getOptionForValue(int value) { + for (GeolocationDataSourceType option : GeolocationDataSourceType.values()) { if (option.getValue() == value) { return option; } diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/MBTilesFileConnector.java b/Core/src/org/sleuthkit/autopsy/geolocation/MBTilesFileConnector.java new file mode 100755 index 0000000000..99f14f5cb1 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/geolocation/MBTilesFileConnector.java @@ -0,0 +1,159 @@ +/* + * 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; + +import java.nio.file.Path; +import java.nio.file.Paths; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import org.jxmapviewer.OSMTileFactoryInfo; +import org.jxmapviewer.viewer.TileFactoryInfo; +import org.openide.util.Exceptions; +import org.sleuthkit.autopsy.geolocation.datamodel.GeoLocationDataException; + +/** + * Wraps the connection to the MBTiles file\sqlite db. + */ +final class MBTilesFileConnector { + private final static String DB_URL = "jdbc:sqlite:%s"; + private final static String TILE_QUERY = "SELECT tile_data FROM images WHERE tile_id = '%s'"; + private final static String FORMAT_QUERY = "SELECT value FROM metadata WHERE name='format'"; + private TileFactoryInfo factoryInfo; + private Connection dbConnection = null; + + /** + * Returns whether or not the file at the given path is a mbtiles file. + * + * @param filePath Absolute path the the file. + * + * @return True if the file is an mbtiles file + * + * @throws SQLException + */ + static boolean isValidMBTileRasterFile(String filePath) throws SQLException{ + String path = filePath.replaceAll("\\\\", "/"); + String url = String.format(DB_URL, path); + + Path p = Paths.get(filePath); + if( !p.toFile().exists()) { + return false; + } + + try(Connection connection = DriverManager.getConnection(url)) { + try(Statement statement = connection.createStatement()){ + ResultSet resultSet = statement.executeQuery(FORMAT_QUERY); + if(resultSet.next()) { + String format = resultSet.getString(1); + return format.equals("jpg"); + } + } + } + return false; + } + + /** + * Construct a new connection to the MBTile file. + * + * @param tileFilePath MBTiles file absolute path + * + * @throws GeoLocationDataException + */ + MBTilesFileConnector(String tileFilePath) throws GeoLocationDataException{ + try{ + String path = tileFilePath.replaceAll("\\\\", "/"); + String url = String.format(DB_URL, path); + dbConnection = DriverManager.getConnection(url); + factoryInfo = new MBTilesInfo(); + } catch(SQLException ex) { + throw new GeoLocationDataException(String.format("Unable to create sql connection to %s", tileFilePath), ex); + } + } + + /** + * Returns the TileFacortyInfo object for the MBTile file. + * + * @return TileFactoryInfo object or null if the connection has been closed. + */ + TileFactoryInfo getInfo() { + return factoryInfo; + } + + /** + * Get the tile for the given tileID. + * + * @param tileID String tile ID in the format of + * zoom/x/y + * + * @return The tile image byte array or null if the tile was not found + * + * @throws GeoLocationDataException + */ + byte[] getTileBytes(String tileID) throws GeoLocationDataException{ + String query = String.format(TILE_QUERY, tileID); + + try (Statement statement = dbConnection.createStatement()) { + ResultSet resultSet = statement.executeQuery(query); + if (resultSet.next()) { + return resultSet.getBytes(1); + } + } catch (SQLException ex) { + throw new GeoLocationDataException(String.format("Failed to get tile %s", tileID), ex); + } + + return null; + } + + /** + * Close the connection to the MBTile file. + */ + void closeConnection() { + + if(dbConnection != null) { + try { + dbConnection.close(); + } catch (SQLException ex) { + Exceptions.printStackTrace(ex); + } + dbConnection = null; + factoryInfo = null; + } + } + + /** + * Overload the existing OSMTileFacotyInfo to return a "url" specific to + * MBTiles. + */ + private final class MBTilesInfo extends OSMTileFactoryInfo { + + MBTilesInfo() { + super("MBTilesFile", ""); + } + + @Override + public String getTileUrl(int x, int y, int zoom) { + // OSM zoom levels are reversed from how the TileFactory deals with + // them. + int osmZoom = getTotalMapZoom() - zoom; + return String.format("%d/%d/%d", osmZoom, x, y); + } + } +} diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/MBTilesTile.java b/Core/src/org/sleuthkit/autopsy/geolocation/MBTilesTile.java new file mode 100755 index 0000000000..84fa143e33 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/geolocation/MBTilesTile.java @@ -0,0 +1,122 @@ +/* + * 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; + +import java.awt.image.BufferedImage; +import java.lang.ref.SoftReference; + +import org.jxmapviewer.viewer.Tile; + +/** + * Provides a MBTile specific implementation of Tile. + * + * This class borrows functionality from Tile. + */ +final class MBTilesTile extends Tile { + + private SoftReference image = new SoftReference<>(null); + private Priority priority = Priority.High; + private boolean loaded = false; + private final String tileID; + + /** + * Construct an empty tile. + * + * @param x + * @param y + * @param zoom + */ + MBTilesTile(int x, int y, int zoom) { + super(x, y, zoom); + tileID = null; + } + + /** + * Construct a new tile. + * + * @param x Tile row + * @param y Tile column + * @param zoom Tile Zoom level + * @param tileID Tile identifier + * @param priority Priority for loading the tile + */ + MBTilesTile(int x, int y, int zoom, String tileID, Priority priority) { + super(x, y, zoom); + this.priority = priority; + this.tileID = tileID; + } + + /** + * Sets the image for this Tile. + * + * @param image + */ + void setImage(BufferedImage image) { + this.image = new SoftReference<>(image); + setLoaded(true); + } + + /** + * Indicates if this tile's underlying image has been successfully loaded + * yet. + * + * @return true if the Tile has been loaded + */ + @Override + public synchronized boolean isLoaded() { + return loaded; + } + + synchronized void setLoaded(boolean loaded) { + boolean old = isLoaded(); + this.loaded = loaded; + firePropertyChange("loaded", old, isLoaded()); + } + + @Override + public BufferedImage getImage() { + BufferedImage img = image.get(); + if (img == null) { + setLoaded(false); + } + return img; + } + + @Override + public Priority getPriority() { + return priority; + } + + @Override + public void setPriority(Priority priority) { + this.priority = priority; + } + + /** + * Overloading the original version of this function to return + * the tileID for this tile. + * + * @return tileID or null if none was set + */ + @Override + public String getURL() { + return tileID; + } + +} diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/MBTilesTileFactory.java b/Core/src/org/sleuthkit/autopsy/geolocation/MBTilesTileFactory.java new file mode 100755 index 0000000000..c895705918 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/geolocation/MBTilesTileFactory.java @@ -0,0 +1,330 @@ +/* + * 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; + +import java.awt.image.BufferedImage; +import java.io.ByteArrayInputStream; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.PriorityBlockingQueue; +import java.util.concurrent.ThreadFactory; +import java.util.logging.Level; + +import javax.imageio.ImageIO; +import javax.swing.SwingUtilities; + +import org.jxmapviewer.viewer.Tile; +import org.jxmapviewer.viewer.TileCache; +import org.jxmapviewer.viewer.TileFactory; +import org.jxmapviewer.viewer.util.GeoUtil; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.geolocation.datamodel.GeoLocationDataException; + +/** + * This TileFactory class borrows from AbstractTileFactory, changing to + * support the getting the tiles from a database instead the web. + * + */ +final class MBTilesTileFactory extends TileFactory { + + private static final Logger logger = Logger.getLogger(MBTilesTileFactory.class.getName()); + + private volatile int pendingTiles = 0; + private int threadPoolSize = 4; + private ExecutorService service; + + private Map tileMap = new HashMap<>(); + + private TileCache cache = new TileCache(); + + private MBTilesFileConnector connector; + + /** + * Construct a new TileFactory for the MBTiles file at the given location. + * + * @param filePath + * @throws GeoLocationDataException + */ + MBTilesTileFactory(String filePath) throws GeoLocationDataException{ + this(new MBTilesFileConnector(filePath)); + } + + /** + * Construct a new TileFacotry. + * + * @param connector + */ + private MBTilesTileFactory(MBTilesFileConnector connector) { + super(connector.getInfo()); + this.connector = connector; + } + + /** + * Returns the tile that is located at the given tile point for this zoom. + * + * @param x Tile column + * @param y Tile row + * @param zoom Current zoom level + * + * @return A new Tile object + * + */ + @Override + public Tile getTile(int x, int y, int zoom) { + return getTile(x, y, zoom, true); + } + + /** + * Returns the tile object for the given location. + * + * @param tpx + * @param tpy + * @param zoom + * @param eagerLoad + * + * @return + */ + private Tile getTile(int tpx, int tpy, int zoom, boolean eagerLoad) { + // wrap the tiles horizontally --> mod the X with the max width + // and use that + int tileX = tpx;// tilePoint.getX(); + int numTilesWide = (int) getMapSize(zoom).getWidth(); + if (tileX < 0) { + tileX = numTilesWide - (Math.abs(tileX) % numTilesWide); + } + + tileX %= numTilesWide; + int tileY = tpy; + + String url = getInfo().getTileUrl(tileX, tileY, zoom); + + Tile.Priority pri = Tile.Priority.High; + if (!eagerLoad) { + pri = Tile.Priority.Low; + } + Tile tile; + if (!tileMap.containsKey(url)) { + // If its not a valid tile location return an empty tile. + if (!GeoUtil.isValidTile(tileX, tileY, zoom, getInfo())) { + tile = new MBTilesTile(tileX, tileY, zoom); + } else { + tile = new MBTilesTile(tileX, tileY, zoom, url, pri); + startLoading(tile); + } + tileMap.put(url, tile); + } else { + tile = tileMap.get(url); + // If the tile is in the tileMap, but the image is not loaded yet, + // bump the priority. + if (tile.getPriority() == Tile.Priority.Low && eagerLoad && !tile.isLoaded()) { + promote(tile); + } + } + + return tile; + } + + /** + * Returns the TileCache. + * + * @return the tile cache + */ + TileCache getTileCache() { + return cache; + } + + /** + * ==== Threaded Tile loading code === + */ + + /** + * Thread pool for loading the tiles + */ + private final BlockingQueue tileQueue = new PriorityBlockingQueue<>(5, new Comparator() { + @Override + public int compare(Tile o1, Tile o2) { + if (o1.getPriority() == Tile.Priority.Low && o2.getPriority() == Tile.Priority.High) { + return 1; + } + if (o1.getPriority() == Tile.Priority.High && o2.getPriority() == Tile.Priority.Low) { + return -1; + } + return 0; + + } + }); + + /** + * Subclasses may override this method to provide their own executor + * services. This method will be called each time a tile needs to be loaded. + * Implementations should cache the ExecutorService when possible. + * + * @return ExecutorService to load tiles with + */ + synchronized ExecutorService getService() { + if (service == null) { + // System.out.println("creating an executor service with a threadpool of size " + threadPoolSize); + service = Executors.newFixedThreadPool(threadPoolSize, new ThreadFactory() { + private int count = 0; + + @Override + public Thread newThread(Runnable r) { + Thread t = new Thread(r, "tile-pool-" + count++); + t.setPriority(Thread.MIN_PRIORITY); + t.setDaemon(true); + return t; + } + }); + } + return service; + } + + @Override + public void dispose() { + if (service != null) { + service.shutdown(); + service = null; + } + + if(connector != null) { + connector.closeConnection(); + connector = null; + } + } + + @Override + protected synchronized void startLoading(Tile tile) { + if (tile.isLoading()) { + return; + } + pendingTiles++; + tile.setLoading(true); + try { + tileQueue.put(tile); + getService().submit(new TileRunner()); + } catch(InterruptedException ex) { + + } + } + + /** + * Increase the priority of this tile so it will be loaded sooner. + * + * @param tile the tile + */ + synchronized void promote(Tile tile) { + if (tileQueue.contains(tile)) { + try { + tileQueue.remove(tile); + tile.setPriority(Tile.Priority.High); + tileQueue.put(tile); + } catch (InterruptedException ex) { + + } + } + } + + /** + * @return the number of pending (loading or queues) tiles + */ + int getPendingTiles() { + return pendingTiles; + } + + /** + * An inner class which actually loads the tiles. Used by the thread queue. + * Subclasses can override this via {@link #createTileRunner(Tile)} if + * necessary. + */ + private class TileRunner implements Runnable { + + /** + * Gets the full URI of a tile. + * + * @param tile the tile + * + * @throws URISyntaxException if the URI is invalid + * @return a URI for the tile + */ + protected URI getURI(Tile tile) throws URISyntaxException { + if (tile.getURL() == null) { + return null; + } + return new URI(tile.getURL()); + } + + @Override + public void run() { + /* + * Attempt to load the tile from . If loading fails, retry + * two more times. If all attempts fail, nothing else is done. This + * way, if there is some kind of failure, the pooled + * thread can try to load other tiles. + */ + final Tile tile = tileQueue.remove(); + + int remainingAttempts = 3; + while (!tile.isLoaded() && remainingAttempts > 0) { + remainingAttempts--; + try { + URI uri = getURI(tile); + BufferedImage img = cache.get(uri); + if (img == null) { + byte[] bimg = connector.getTileBytes(uri.toString()); + if (bimg != null) { + img = ImageIO.read(new ByteArrayInputStream(bimg)); + cache.put(uri, bimg, img); + img = cache.get(uri); + } + } + if (img == null) { + logger.log(Level.INFO, String.format("Failed to load: %s", uri)); + } else { + final BufferedImage image = img; + SwingUtilities.invokeAndWait(new Runnable() { + @Override + public void run() { + if (tile instanceof MBTilesTile) { + ((MBTilesTile) tile).setImage(image); + } + pendingTiles--; + fireTileLoadedEvent(tile); + } + }); + } + } catch (OutOfMemoryError memErr) { + cache.needMoreMemory(); + } catch (Throwable ex) { + if (remainingAttempts == 0) { + logger.log(Level.SEVERE, String.format("Failed to load a tile at URL: %s, stopping", tile.getURL()), ex); + } else { + logger.log(Level.WARNING, "Failed to load a tile at URL: " + tile.getURL() + ", retrying", ex); + } + } + } + tile.setLoading(false); + } + } +} diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/MapPanel.java b/Core/src/org/sleuthkit/autopsy/geolocation/MapPanel.java index fb5cdc23e5..16f1169e71 100755 --- a/Core/src/org/sleuthkit/autopsy/geolocation/MapPanel.java +++ b/Core/src/org/sleuthkit/autopsy/geolocation/MapPanel.java @@ -56,7 +56,6 @@ import org.jxmapviewer.viewer.TileFactory; import org.jxmapviewer.viewer.TileFactoryInfo; import org.jxmapviewer.viewer.Waypoint; import org.jxmapviewer.viewer.WaypointPainter; -import org.jxmapviewer.viewer.util.GeoUtil; import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.core.UserPreferences; import org.sleuthkit.autopsy.coreutils.Logger; @@ -113,7 +112,10 @@ final public class MapPanel extends javax.swing.JPanel { @Override public void preferenceChange(PreferenceChangeEvent evt) { try { - mapViewer.setTileFactory(new DefaultTileFactory(getTileFactoryInfo())); + // Tell the old factory to cleanup + mapViewer.getTileFactory().dispose(); + + mapViewer.setTileFactory(getTileFactory()); initializeZoomSlider(); } catch (GeoLocationDataException ex) { logger.log(Level.SEVERE, "Failed to connect to new geolocation tile server.", ex); //NON-NLS @@ -134,8 +136,7 @@ final public class MapPanel extends javax.swing.JPanel { */ void initMap() throws GeoLocationDataException { - TileFactoryInfo info = getTileFactoryInfo(); - DefaultTileFactory tileFactory = new DefaultTileFactory(info); + TileFactory tileFactory = getTileFactory(); mapViewer.setTileFactory(tileFactory); // Add Mouse interactions @@ -192,21 +193,23 @@ final public class MapPanel extends javax.swing.JPanel { } /** - * Create the TileFactoryInfo object based on the user preference. + * Create the TileFactory object based on the user preference. * * @return */ - TileFactoryInfo getTileFactoryInfo() throws GeoLocationDataException { - switch (GeolocationSettingsPanel.GeolocationTileOption.getOptionForValue(UserPreferences.getGeolocationtTileOption())) { + private TileFactory getTileFactory() throws GeoLocationDataException { + switch (GeolocationSettingsPanel.GeolocationDataSourceType.getOptionForValue(UserPreferences.getGeolocationtTileOption())) { case ONLINE_USER_DEFINED_OSM_SERVER: - return createOnlineOSMFactory(UserPreferences.getGeolocationOsmServerAddress()); + return new DefaultTileFactory(createOnlineOSMFactory(UserPreferences.getGeolocationOsmServerAddress())); case OFFLINE_OSM_ZIP: - return createOSMZipFactory(UserPreferences.getGeolocationOsmZipPath()); + return new DefaultTileFactory(createOSMZipFactory(UserPreferences.getGeolocationOsmZipPath())); + case OFFILE_MBTILES_FILE: + return new MBTilesTileFactory(UserPreferences.getGeolocationMBTilesFilePath()); default: - return new VirtualEarthTileFactoryInfo(VirtualEarthTileFactoryInfo.MAP); + return new DefaultTileFactory(new VirtualEarthTileFactoryInfo(VirtualEarthTileFactoryInfo.MAP)); } } - + /** * Create the TileFactoryInfo for an online OSM tile server. * @@ -221,9 +224,6 @@ final public class MapPanel extends javax.swing.JPanel { throw new GeoLocationDataException("Invalid user preference for OSM user define tile server. Address is an empty string."); } else { TileFactoryInfo info = new OSMTileFactoryInfo("User Defined Server", address); - if (!GeoUtil.isValidTile(1, 1, 1, info)) { - throw new GeoLocationDataException(String.format("Invalid OSM user define tile server: %s", address)); - } return info; } }