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.LinkedHashSet;
39 import java.util.List;
41 import java.util.logging.Level;
42 import java.util.prefs.PreferenceChangeEvent;
43 import java.util.prefs.PreferenceChangeListener;
44 import javax.swing.JMenuItem;
45 import javax.swing.JOptionPane;
46 import javax.swing.JPopupMenu;
47 import javax.swing.JSeparator;
48 import javax.swing.Popup;
49 import javax.swing.PopupFactory;
50 import javax.swing.Timer;
51 import javax.swing.event.MouseInputListener;
52 import org.jxmapviewer.JXMapViewer;
53 import org.jxmapviewer.OSMTileFactoryInfo;
54 import org.jxmapviewer.VirtualEarthTileFactoryInfo;
55 import org.jxmapviewer.input.CenterMapListener;
56 import org.jxmapviewer.input.ZoomMouseWheelListenerCursor;
57 import org.jxmapviewer.viewer.DefaultTileFactory;
58 import org.jxmapviewer.viewer.GeoPosition;
59 import org.jxmapviewer.viewer.TileFactory;
60 import org.jxmapviewer.viewer.TileFactoryInfo;
61 import org.jxmapviewer.viewer.Waypoint;
62 import org.jxmapviewer.viewer.WaypointPainter;
63 import org.jxmapviewer.viewer.WaypointRenderer;
64 import org.openide.util.NbBundle.Messages;
70 import javax.imageio.ImageIO;
71 import javax.swing.SwingUtilities;
72 import org.jxmapviewer.viewer.DefaultWaypointRenderer;
77 final public class MapPanel extends javax.swing.JPanel {
79 static final String CURRENT_MOUSE_GEOPOSITION =
"CURRENT_MOUSE_GEOPOSITION";
99 "MapPanel_connection_failure_message=Failed to connect to new geolocation map tile source.",
100 "MapPanel_connection_failure_message_title=Connection Failure"
105 zoomChanging =
false;
107 popupFactory =
new PopupFactory();
115 public void resizeTimedOut() {
122 public void preferenceChange(PreferenceChangeEvent evt) {
128 initializeZoomSlider();
130 logger.log(Level.SEVERE,
"Failed to connect to new geolocation tile server.", ex);
131 JOptionPane.showMessageDialog(
MapPanel.this,
132 Bundle.MapPanel_connection_failure_message(),
133 Bundle.MapPanel_connection_failure_message_title(),
134 JOptionPane.ERROR_MESSAGE);
136 Bundle.MapPanel_connection_failure_message_title(),
137 Bundle.MapPanel_connection_failure_message());
150 List<MapWaypoint> getVisibleWaypoints() {
152 Rectangle viewport =
mapViewer.getViewportBounds();
153 List<MapWaypoint> waypoints =
new ArrayList<>();
155 Iterator<MapWaypoint> iterator = waypointTree.
iterator();
156 while (iterator.hasNext()) {
157 MapWaypoint waypoint = iterator.next();
158 if (viewport.contains(
mapViewer.getTileFactory().geoToPixel(waypoint.getPosition(),
mapViewer.getZoom()))) {
159 waypoints.add(waypoint);
169 void initMap() throws GeoLocationDataException {
175 MouseInputListener mia =
new MapPanMouseInputListener(
mapViewer);
183 mapViewer.addPropertyChangeListener(
"zoom",
new PropertyChangeListener() {
185 public void propertyChange(PropertyChangeEvent evt) {
190 zoomSlider.setMinimum(tileFactory.getInfo().getMinimumZoomLevel());
191 zoomSlider.setMaximum(tileFactory.getInfo().getMaximumZoomLevel());
193 setZoom(tileFactory.getInfo().getMaximumZoomLevel() - 1);
195 mapViewer.setCenterPosition(
new GeoPosition(0, 0));
198 WaypointPainter<Waypoint> waypointPainter =
new WaypointPainter<Waypoint>() {
200 public Set<Waypoint> getWaypoints() {
203 Set<Waypoint> set =
new LinkedHashSet<>();
204 if (waypointTree != null) {
205 Iterator<MapWaypoint> iterator = waypointTree.
iterator();
206 while (iterator.hasNext()) {
207 MapWaypoint point = iterator.next();
212 if (currentlySelectedWaypoint != null) {
213 set.remove(currentlySelectedWaypoint);
214 set.add(currentlySelectedWaypoint);
222 waypointPainter.setRenderer(
new MapWaypointRenderer());
223 }
catch (IOException ex) {
224 logger.log(Level.WARNING,
"Failed to load waypoint image resource, using DefaultWaypointRenderer", ex);
225 waypointPainter.setRenderer(
new DefaultWaypointRenderer());
228 mapViewer.setOverlayPainter(waypointPainter);
234 void initializeZoomSlider() {
235 TileFactory tileFactory =
mapViewer.getTileFactory();
236 zoomSlider.setMinimum(tileFactory.getInfo().getMinimumZoomLevel());
237 zoomSlider.setMaximum(tileFactory.getInfo().getMaximumZoomLevel());
250 case ONLINE_USER_DEFINED_OSM_SERVER:
252 case OFFLINE_OSM_ZIP:
254 case OFFILE_MBTILES_FILE:
257 return new DefaultTileFactory(
new VirtualEarthTileFactoryInfo(VirtualEarthTileFactoryInfo.MAP));
271 if (address.isEmpty()) {
272 throw new GeoLocationDataException(
"Invalid user preference for OSM user define tile server. Address is an empty string.");
274 TileFactoryInfo info =
new OSMTileFactoryInfo(
"User Defined Server", address);
289 if (path.isEmpty()) {
290 throw new GeoLocationDataException(
"Invalid OSM tile Zip file. User preference value is empty string.");
292 File file =
new File(path);
293 if (!file.exists() || !file.canRead()) {
294 throw new GeoLocationDataException(
"Invalid OSM tile zip file. Unable to read file: " + path);
297 String zipPath = path.replaceAll(
"\\\\",
"/");
299 return new OSMTileFactoryInfo(
"ZIP archive",
"jar:file:/" + zipPath +
"!");
308 void setWaypoints(List<MapWaypoint> waypoints) {
311 for (MapWaypoint waypoint : waypoints) {
312 waypointTree.
add(waypoint);
323 void setZoom(
int zoom) {
327 zoomChanging =
false;
333 void clearWaypoints() {
335 currentlySelectedWaypoint = null;
336 if (currentPopup != null) {
351 MapWaypoint waypoint = null;
352 if(waypoints.size() > 0) {
353 waypoint = waypoints.get(0);
358 if (waypoint != null && !waypoint.equals(currentlySelectedWaypoint)) {
359 currentlySelectedWaypoint = waypoint;
360 if(currentPopup != null) {
365 }
catch (TskCoreException ex) {
366 logger.log(Level.WARNING,
"Failed to show popup for waypoint", ex);
376 private void showPopupMenu(MapWaypoint waypoint, Point point)
throws TskCoreException {
377 if (waypoint == null) {
381 JMenuItem[] items = waypoint.getMenuItems();
382 JPopupMenu popupMenu =
new JPopupMenu();
383 for (JMenuItem menu : items) {
388 popupMenu.add(
new JSeparator());
391 popupMenu.show(
mapViewer, point.x, point.y);
398 if (currentlySelectedWaypoint != null) {
399 if (currentPopup != null) {
403 WaypointDetailPanel detailPane =
new WaypointDetailPanel();
404 detailPane.setWaypoint(currentlySelectedWaypoint);
405 detailPane.setPreferredSize(
new Dimension(POPUP_WIDTH, POPUP_HEIGHT));
406 detailPane.addActionListener(
new ActionListener() {
408 public void actionPerformed(ActionEvent e) {
409 if (currentPopup != null) {
419 currentPopup = popupFactory.getPopup(
this, detailPane, popupLocation.x, popupLocation.y);
433 Point panelCorner = this.getLocationOnScreen();
434 int width = this.getWidth();
436 int popupX = panelCorner.x + width - POPUP_WIDTH -
POPUP_MARGIN;
439 return new Point(popupX, popupY);
451 if (waypointTree == null) {
456 GeoPosition geopos =
mapViewer.getTileFactory().pixelToGeo(mouseClickPoint,
mapViewer.getZoom());
459 Collection<MapWaypoint> waypoints = waypointTree.
nearestNeighbourSearch(10, MapWaypoint.getDummyWaypoint(geopos));
461 if (waypoints == null || waypoints.isEmpty()) {
465 Iterator<MapWaypoint> iterator = waypoints.iterator();
469 List<MapWaypoint> closestPoints =
new ArrayList<>();
470 while (iterator.hasNext()) {
471 MapWaypoint nextWaypoint = iterator.next();
473 Point2D point =
mapViewer.getTileFactory().geoToPixel(nextWaypoint.getPosition(),
mapViewer.getZoom());
475 Rectangle rect =
mapViewer.getViewportBounds();
476 Point converted_gp_pt =
new Point((
int) point.getX() - rect.x,
477 (int) point.getY() - rect.y);
479 if (converted_gp_pt.distance(mouseClickPoint) < 10) {
480 closestPoints.add(nextWaypoint);
484 return closestPoints;
497 extends ComponentAdapter
498 implements ActionListener {
517 timer =
new Timer(delayMS,
this);
518 timer.setRepeats(
false);
519 timer.setCoalesce(
false);
543 @SuppressWarnings(
"unchecked")
546 java.awt.GridBagConstraints gridBagConstraints;
553 setLayout(
new java.awt.BorderLayout());
555 mapViewer.addMouseMotionListener(
new java.awt.event.MouseMotionAdapter() {
556 public void mouseMoved(java.awt.event.MouseEvent evt) {
560 mapViewer.addMouseListener(
new java.awt.event.MouseAdapter() {
561 public void mouseClicked(java.awt.event.MouseEvent evt) {
564 public void mousePressed(java.awt.event.MouseEvent evt) {
567 public void mouseReleased(java.awt.event.MouseEvent evt) {
571 mapViewer.setLayout(
new java.awt.GridBagLayout());
580 zoomSlider.setOrientation(javax.swing.JSlider.VERTICAL);
583 zoomSlider.setMinimumSize(
new java.awt.Dimension(35, 100));
585 zoomSlider.setPreferredSize(
new java.awt.Dimension(35, 190));
586 zoomSlider.addChangeListener(
new javax.swing.event.ChangeListener() {
587 public void stateChanged(javax.swing.event.ChangeEvent evt) {
592 javax.swing.GroupLayout zoomPanelLayout =
new javax.swing.GroupLayout(
zoomPanel);
594 zoomPanelLayout.setHorizontalGroup(
595 zoomPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
596 .addGroup(zoomPanelLayout.createSequentialGroup()
598 .addComponent(
zoomSlider, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
599 .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
601 zoomPanelLayout.setVerticalGroup(
602 zoomPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
603 .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, zoomPanelLayout.createSequentialGroup()
604 .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
605 .addComponent(
zoomSlider, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
609 gridBagConstraints =
new java.awt.GridBagConstraints();
610 gridBagConstraints.anchor = java.awt.GridBagConstraints.SOUTHWEST;
611 gridBagConstraints.weightx = 1.0;
612 gridBagConstraints.weighty = 1.0;
613 gridBagConstraints.insets =
new java.awt.Insets(4, 4, 4, 4);
616 add(
mapViewer, java.awt.BorderLayout.CENTER);
626 if (evt.isPopupTrigger()) {
632 if (evt.isPopupTrigger()) {
638 GeoPosition geopos =
mapViewer.getTileFactory().pixelToGeo(evt.getPoint(),
mapViewer.getZoom());
639 firePropertyChange(CURRENT_MOUSE_GEOPOSITION, null, geopos);
643 if(!evt.isPopupTrigger() && SwingUtilities.isLeftMouseButton(evt)) {
645 if(waypoints.size() > 0) {
646 currentlySelectedWaypoint = waypoints.get(0);
675 defaultWaypointImage = ImageIO.read(getClass().getResource(
"/org/sleuthkit/autopsy/images/waypoint_teal.png"));
676 selectedWaypointImage = ImageIO.read(getClass().getResource(
"/org/sleuthkit/autopsy/images/waypoint_yellow.png"));
681 Point2D point = jxmv.getTileFactory().geoToPixel(waypoint.getPosition(), jxmv.getZoom());
683 int x = (int)point.getX();
684 int y = (int)point.getY();
686 BufferedImage image = (waypoint == currentlySelectedWaypoint ? selectedWaypointImage:
defaultWaypointImage);
688 (gd.create()).drawImage(image, x -image.getWidth() / 2, y -image.getHeight(), null);
final BufferedImage defaultWaypointImage
abstract void resizeTimedOut()
static String getGeolocationOsmZipPath()
final PopupFactory popupFactory
void showPopupMenu(MapWaypoint waypoint, Point point)
void paintWaypoint(Graphics2D gd, JXMapViewer jxmv, Waypoint waypoint)
void zoomSliderStateChanged(javax.swing.event.ChangeEvent evt)
static final int POPUP_WIDTH
void componentResized(ComponentEvent e)
void mapViewerMouseReleased(java.awt.event.MouseEvent evt)
ComponentResizeEndListener()
static final int POPUP_MARGIN
final BufferedImage selectedWaypointImage
List< MapWaypoint > findClosestWaypoint(Point mouseClickPoint)
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)
Collection< T > nearestNeighbourSearch(int K, T value)
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)
static void addChangeListener(PreferenceChangeListener listener)
ComponentResizeEndListener(int delayMS)
static final long serialVersionUID
org.jxmapviewer.JXMapViewer mapViewer
void showPopupMenu(Point point)
MapWaypoint currentlySelectedWaypoint