19 package org.sleuthkit.autopsy.geolocation;
21 import java.awt.Dimension;
22 import java.awt.Graphics2D;
23 import java.awt.Point;
24 import java.awt.Rectangle;
25 import java.awt.event.ActionEvent;
26 import java.awt.event.ActionListener;
27 import java.awt.event.ComponentAdapter;
28 import java.awt.event.ComponentEvent;
29 import java.awt.geom.Point2D;
30 import java.awt.image.BufferedImage;
31 import java.beans.PropertyChangeEvent;
32 import java.beans.PropertyChangeListener;
34 import java.io.IOException;
35 import java.util.ArrayList;
36 import java.util.Collection;
37 import java.util.Iterator;
38 import java.util.List;
40 import java.util.logging.Level;
41 import java.util.prefs.PreferenceChangeEvent;
42 import java.util.prefs.PreferenceChangeListener;
43 import javax.swing.JMenuItem;
44 import javax.swing.JOptionPane;
45 import javax.swing.JPopupMenu;
46 import javax.swing.JSeparator;
47 import javax.swing.Popup;
48 import javax.swing.PopupFactory;
49 import javax.swing.Timer;
50 import javax.swing.event.MouseInputListener;
51 import org.jxmapviewer.JXMapViewer;
52 import org.jxmapviewer.OSMTileFactoryInfo;
53 import org.jxmapviewer.VirtualEarthTileFactoryInfo;
54 import org.jxmapviewer.input.CenterMapListener;
55 import org.jxmapviewer.input.ZoomMouseWheelListenerCursor;
56 import org.jxmapviewer.viewer.DefaultTileFactory;
57 import org.jxmapviewer.viewer.GeoPosition;
58 import org.jxmapviewer.viewer.TileFactory;
59 import org.jxmapviewer.viewer.TileFactoryInfo;
60 import org.jxmapviewer.viewer.WaypointPainter;
61 import org.jxmapviewer.viewer.WaypointRenderer;
62 import org.openide.util.NbBundle.Messages;
68 import javax.imageio.ImageIO;
69 import javax.swing.SwingUtilities;
74 final public class MapPanel extends javax.swing.JPanel {
76 static final String CURRENT_MOUSE_GEOPOSITION =
"CURRENT_MOUSE_GEOPOSITION";
100 "MapPanel_connection_failure_message=Failed to connect to new geolocation map tile source.",
101 "MapPanel_connection_failure_message_title=Connection Failure"
106 zoomChanging =
false;
108 popupFactory =
new PopupFactory();
111 defaultWaypointImage = ImageIO.read(getClass().getResource(
"/org/sleuthkit/autopsy/images/waypoint_teal.png"));
112 selectedWaypointImage = ImageIO.read(getClass().getResource(
"/org/sleuthkit/autopsy/images/waypoint_yellow.png"));
113 }
catch (IOException ex) {
114 logger.log(Level.WARNING,
"Unable to load geolocation waypoint images", ex);
123 public void resizeTimedOut() {
130 public void preferenceChange(PreferenceChangeEvent evt) {
136 initializeZoomSlider();
138 logger.log(Level.SEVERE,
"Failed to connect to new geolocation tile server.", ex);
139 JOptionPane.showMessageDialog(
MapPanel.this,
140 Bundle.MapPanel_connection_failure_message(),
141 Bundle.MapPanel_connection_failure_message_title(),
142 JOptionPane.ERROR_MESSAGE);
144 Bundle.MapPanel_connection_failure_message_title(),
145 Bundle.MapPanel_connection_failure_message());
156 List<MapWaypoint> getVisibleWaypoints() {
158 Rectangle viewport =
mapViewer.getViewportBounds();
159 List<MapWaypoint> waypoints =
new ArrayList<>();
161 Iterator<MapWaypoint> iterator = waypointTree.
iterator();
162 while (iterator.hasNext()) {
163 MapWaypoint waypoint = iterator.next();
164 if (viewport.contains(
mapViewer.getTileFactory().geoToPixel(waypoint.getPosition(),
mapViewer.getZoom()))) {
165 waypoints.add(waypoint);
175 void initMap() throws GeoLocationDataException {
181 MouseInputListener mia =
new MapPanMouseInputListener(
mapViewer);
189 mapViewer.addPropertyChangeListener(
"zoom",
new PropertyChangeListener() {
191 public void propertyChange(PropertyChangeEvent evt) {
196 zoomSlider.setMinimum(tileFactory.getInfo().getMinimumZoomLevel());
197 zoomSlider.setMaximum(tileFactory.getInfo().getMaximumZoomLevel());
199 setZoom(tileFactory.getInfo().getMaximumZoomLevel() - 1);
201 mapViewer.setCenterPosition(
new GeoPosition(0, 0));
204 WaypointPainter<MapWaypoint> waypointPainter =
new WaypointPainter<MapWaypoint>() {
206 public Set<MapWaypoint> getWaypoints() {
207 if (currentlySelectedWaypoint != null) {
208 waypointSet.remove(currentlySelectedWaypoint);
209 waypointSet.add(currentlySelectedWaypoint);
214 waypointPainter.setRenderer(
new MapWaypointRenderer());
216 mapViewer.setOverlayPainter(waypointPainter);
222 void initializeZoomSlider() {
223 TileFactory tileFactory =
mapViewer.getTileFactory();
224 zoomSlider.setMinimum(tileFactory.getInfo().getMinimumZoomLevel());
225 zoomSlider.setMaximum(tileFactory.getInfo().getMaximumZoomLevel());
238 case ONLINE_USER_DEFINED_OSM_SERVER:
240 case OFFLINE_OSM_ZIP:
242 case OFFILE_MBTILES_FILE:
245 return new DefaultTileFactory(
new VirtualEarthTileFactoryInfo(VirtualEarthTileFactoryInfo.MAP));
259 if (address.isEmpty()) {
260 throw new GeoLocationDataException(
"Invalid user preference for OSM user define tile server. Address is an empty string.");
262 TileFactoryInfo info =
new OSMTileFactoryInfo(
"User Defined Server", address);
277 if (path.isEmpty()) {
278 throw new GeoLocationDataException(
"Invalid OSM tile Zip file. User preference value is empty string.");
280 File file =
new File(path);
281 if (!file.exists() || !file.canRead()) {
282 throw new GeoLocationDataException(
"Invalid OSM tile zip file. Unable to read file: " + path);
285 String zipPath = path.replaceAll(
"\\\\",
"/");
287 return new OSMTileFactoryInfo(
"ZIP archive",
"jar:file:/" + zipPath +
"!");
296 void setWaypoints(Set<MapWaypoint> waypoints) {
298 this.waypointSet = waypoints;
299 for (MapWaypoint waypoint : waypoints) {
300 waypointTree.
add(waypoint);
310 void setZoom(
int zoom) {
314 zoomChanging =
false;
320 void clearWaypoints() {
322 currentlySelectedWaypoint = null;
323 if (currentPopup != null) {
338 MapWaypoint waypoint = null;
339 if (waypoints.size() > 0) {
340 waypoint = waypoints.get(0);
345 if (waypoint != null && !waypoint.equals(currentlySelectedWaypoint)) {
346 currentlySelectedWaypoint = waypoint;
347 if (currentPopup != null) {
352 }
catch (TskCoreException ex) {
353 logger.log(Level.WARNING,
"Failed to show popup for waypoint", ex);
363 private void showPopupMenu(MapWaypoint waypoint, Point point)
throws TskCoreException {
364 if (waypoint == null) {
368 JMenuItem[] items = waypoint.getMenuItems();
369 JPopupMenu popupMenu =
new JPopupMenu();
370 for (JMenuItem menu : items) {
375 popupMenu.add(
new JSeparator());
378 popupMenu.show(
mapViewer, point.x, point.y);
385 if (currentlySelectedWaypoint != null) {
386 if (currentPopup != null) {
390 WaypointDetailPanel detailPane =
new WaypointDetailPanel();
391 detailPane.setWaypoint(currentlySelectedWaypoint);
392 detailPane.setPreferredSize(
new Dimension(POPUP_WIDTH, POPUP_HEIGHT));
393 detailPane.addActionListener(
new ActionListener() {
395 public void actionPerformed(ActionEvent e) {
396 if (currentPopup != null) {
406 currentPopup = popupFactory.getPopup(
this, detailPane, popupLocation.x, popupLocation.y);
410 if (currentPopup != null) {
425 Point panelCorner = this.getLocationOnScreen();
426 int width = this.getWidth();
428 int popupX = panelCorner.x + width - POPUP_WIDTH -
POPUP_MARGIN;
431 return new Point(popupX, popupY);
443 if (waypointTree == null) {
444 return new ArrayList<>();
448 GeoPosition geopos =
mapViewer.convertPointToGeoPosition(clickPoint);
451 Collection<MapWaypoint> waypoints = waypointTree.
nearestNeighbourSearch(1, MapWaypoint.getDummyWaypoint(geopos));
453 if (waypoints == null || waypoints.isEmpty()) {
457 Iterator<MapWaypoint> iterator = waypoints.iterator();
461 List<MapWaypoint> closestPoints =
new ArrayList<>();
462 while (iterator.hasNext()) {
463 MapWaypoint nextWaypoint = iterator.next();
465 Point2D point =
mapViewer.convertGeoPositionToPoint(nextWaypoint.getPosition());
466 Rectangle rect =
new Rectangle((
int) point.getX() - (defaultWaypointImage.getWidth() / 2), (
int) point.getY() - defaultWaypointImage.getHeight(), defaultWaypointImage.getWidth(), defaultWaypointImage.getHeight());
468 if (rect.contains(clickPoint)) {
469 closestPoints.add(nextWaypoint);
473 return closestPoints;
486 extends ComponentAdapter
487 implements ActionListener {
506 timer =
new Timer(delayMS,
this);
507 timer.setRepeats(
false);
508 timer.setCoalesce(
false);
532 @SuppressWarnings(
"unchecked")
535 java.awt.GridBagConstraints gridBagConstraints;
540 javax.swing.JButton zoomInBtn =
new javax.swing.JButton();
541 javax.swing.JButton zoomOutBtn =
new javax.swing.JButton();
544 setLayout(
new java.awt.BorderLayout());
546 mapViewer.addMouseMotionListener(
new java.awt.event.MouseMotionAdapter() {
547 public void mouseMoved(java.awt.event.MouseEvent evt) {
551 mapViewer.addMouseListener(
new java.awt.event.MouseAdapter() {
552 public void mouseClicked(java.awt.event.MouseEvent evt) {
555 public void mousePressed(java.awt.event.MouseEvent evt) {
558 public void mouseReleased(java.awt.event.MouseEvent evt) {
562 mapViewer.setLayout(
new java.awt.GridBagLayout());
567 zoomPanel.setLayout(
new java.awt.GridBagLayout());
572 zoomSlider.setOrientation(javax.swing.JSlider.VERTICAL);
575 zoomSlider.setMinimumSize(
new java.awt.Dimension(35, 100));
577 zoomSlider.setPreferredSize(
new java.awt.Dimension(35, 190));
578 zoomSlider.addChangeListener(
new javax.swing.event.ChangeListener() {
579 public void stateChanged(javax.swing.event.ChangeEvent evt) {
583 gridBagConstraints =
new java.awt.GridBagConstraints();
584 gridBagConstraints.gridx = 0;
585 gridBagConstraints.gridy = 1;
588 zoomInBtn.setIcon(
new javax.swing.ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/images/plus-grey.png")));
589 org.openide.awt.Mnemonics.setLocalizedText(zoomInBtn,
org.openide.util.NbBundle.getMessage(
MapPanel.class,
"MapPanel.zoomInBtn.text"));
590 zoomInBtn.setBorder(null);
591 zoomInBtn.setBorderPainted(
false);
592 zoomInBtn.setFocusPainted(
false);
593 zoomInBtn.setRequestFocusEnabled(
false);
594 zoomInBtn.setRolloverEnabled(
false);
595 zoomInBtn.addActionListener(
new java.awt.event.ActionListener() {
596 public void actionPerformed(java.awt.event.ActionEvent evt) {
600 gridBagConstraints =
new java.awt.GridBagConstraints();
601 gridBagConstraints.gridx = 0;
602 gridBagConstraints.gridy = 0;
603 zoomPanel.add(zoomInBtn, gridBagConstraints);
605 zoomOutBtn.setIcon(
new javax.swing.ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/images/minus-grey.png")));
606 org.openide.awt.Mnemonics.setLocalizedText(zoomOutBtn,
org.openide.util.NbBundle.getMessage(
MapPanel.class,
"MapPanel.zoomOutBtn.text"));
607 zoomOutBtn.setBorder(null);
608 zoomOutBtn.setBorderPainted(
false);
609 zoomOutBtn.setFocusPainted(
false);
610 zoomOutBtn.setRequestFocusEnabled(
false);
611 zoomOutBtn.setRolloverEnabled(
false);
612 zoomOutBtn.addActionListener(
new java.awt.event.ActionListener() {
613 public void actionPerformed(java.awt.event.ActionEvent evt) {
617 gridBagConstraints =
new java.awt.GridBagConstraints();
618 gridBagConstraints.gridx = 0;
619 gridBagConstraints.gridy = 2;
620 zoomPanel.add(zoomOutBtn, gridBagConstraints);
622 gridBagConstraints =
new java.awt.GridBagConstraints();
623 gridBagConstraints.anchor = java.awt.GridBagConstraints.SOUTHWEST;
624 gridBagConstraints.weightx = 1.0;
625 gridBagConstraints.weighty = 1.0;
626 gridBagConstraints.insets =
new java.awt.Insets(4, 4, 4, 4);
629 add(
mapViewer, java.awt.BorderLayout.CENTER);
639 if (evt.isPopupTrigger()) {
645 if (evt.isPopupTrigger()) {
651 GeoPosition geopos =
mapViewer.convertPointToGeoPosition(evt.getPoint());
652 firePropertyChange(CURRENT_MOUSE_GEOPOSITION, null, geopos);
656 if (!evt.isPopupTrigger() && SwingUtilities.isLeftMouseButton(evt)) {
658 if (waypoints.size() > 0) {
659 currentlySelectedWaypoint = waypoints.get(0);
661 currentlySelectedWaypoint = null;
669 setZoom(currentValue - 1);
674 setZoom(currentValue + 1);
690 public void paintWaypoint(Graphics2D gd, JXMapViewer jxmv, MapWaypoint waypoint) {
691 Point2D point = jxmv.getTileFactory().geoToPixel(waypoint.getPosition(), jxmv.getZoom());
693 int x = (int) point.getX();
694 int y = (int) point.getY();
696 BufferedImage image = (waypoint == currentlySelectedWaypoint ? selectedWaypointImage :
defaultWaypointImage);
698 (gd.create()).drawImage(image, x - image.getWidth() / 2, y - image.getHeight(), null);
void zoomInBtnActionPerformed(java.awt.event.ActionEvent evt)
abstract void resizeTimedOut()
static String getGeolocationOsmZipPath()
final PopupFactory popupFactory
BufferedImage selectedWaypointImage
void showPopupMenu(MapWaypoint waypoint, Point point)
void zoomSliderStateChanged(javax.swing.event.ChangeEvent evt)
static final int POPUP_WIDTH
void componentResized(ComponentEvent e)
void mapViewerMouseReleased(java.awt.event.MouseEvent evt)
Collection< T > nearestNeighbourSearch(int numNeighbors, T value)
ComponentResizeEndListener()
static final int POPUP_MARGIN
javax.swing.JPanel zoomPanel
static final int DEFAULT_TIMEOUT
KdTree< MapWaypoint > waypointTree
static String getGeolocationMBTilesFilePath()
TileFactoryInfo createOnlineOSMFactory(String address)
static final Logger logger
javax.swing.JSlider zoomSlider
void mapViewerMouseClicked(java.awt.event.MouseEvent evt)
void mapViewerMousePressed(java.awt.event.MouseEvent evt)
void mapViewerMouseMoved(java.awt.event.MouseEvent evt)
void paintWaypoint(Graphics2D gd, JXMapViewer jxmv, MapWaypoint waypoint)
List< MapWaypoint > findClosestWaypoint(Point clickPoint)
static int getGeolocationtTileOption()
Point getLocationForDetailsPopup()
static final int POPUP_HEIGHT
void actionPerformed(ActionEvent e)
TileFactoryInfo createOSMZipFactory(String path)
static String getGeolocationOsmServerAddress()
static void error(String title, String message)
TileFactory getTileFactory()
synchronized static Logger getLogger(String name)
void zoomOutBtnActionPerformed(java.awt.event.ActionEvent evt)
static void addChangeListener(PreferenceChangeListener listener)
ComponentResizeEndListener(int delayMS)
Set< MapWaypoint > waypointSet
static final long serialVersionUID
org.jxmapviewer.JXMapViewer mapViewer
void showPopupMenu(Point point)
MapWaypoint currentlySelectedWaypoint
BufferedImage defaultWaypointImage