Autopsy 4.22.1
Graphical digital forensics platform for The Sleuth Kit and other tools.
MapPanel.java
Go to the documentation of this file.
1/*
2 * Autopsy Forensic Browser
3 *
4 * Copyright 2019 Basis Technology Corp.
5 * Contact: carrier <at> sleuthkit <dot> org
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 */
19package org.sleuthkit.autopsy.geolocation;
20
21import java.awt.AlphaComposite;
22import java.awt.BasicStroke;
23import java.awt.Color;
24import java.awt.Dimension;
25import java.awt.Graphics2D;
26import java.awt.Paint;
27import java.awt.Point;
28import java.awt.Rectangle;
29import java.awt.RenderingHints;
30import java.awt.event.ActionEvent;
31import java.awt.event.ActionListener;
32import java.awt.event.ComponentAdapter;
33import java.awt.event.ComponentEvent;
34import java.awt.geom.GeneralPath;
35import java.awt.geom.Point2D;
36import java.awt.image.BufferedImage;
37import java.beans.PropertyChangeEvent;
38import java.beans.PropertyChangeListener;
39import java.io.File;
40import java.io.IOException;
41import java.util.ArrayList;
42import java.util.Collection;
43import java.util.HashMap;
44import java.util.HashSet;
45import java.util.Iterator;
46import java.util.List;
47import java.util.Map;
48import java.util.Set;
49import java.util.logging.Level;
50import java.util.prefs.PreferenceChangeEvent;
51import java.util.prefs.PreferenceChangeListener;
52import javax.swing.JMenuItem;
53import javax.swing.JOptionPane;
54import javax.swing.JPopupMenu;
55import javax.swing.JSeparator;
56import javax.swing.Popup;
57import javax.swing.PopupFactory;
58import javax.swing.Timer;
59import javax.swing.event.MouseInputListener;
60import org.jxmapviewer.JXMapViewer;
61import org.jxmapviewer.OSMTileFactoryInfo;
62import org.jxmapviewer.VirtualEarthTileFactoryInfo;
63import org.jxmapviewer.input.CenterMapListener;
64import org.jxmapviewer.input.ZoomMouseWheelListenerCursor;
65import org.jxmapviewer.viewer.DefaultTileFactory;
66import org.jxmapviewer.viewer.GeoPosition;
67import org.jxmapviewer.viewer.TileFactory;
68import org.jxmapviewer.viewer.TileFactoryInfo;
69import org.jxmapviewer.viewer.WaypointPainter;
70import org.jxmapviewer.viewer.WaypointRenderer;
71import org.openide.util.NbBundle.Messages;
72import org.sleuthkit.autopsy.core.UserPreferences;
73import org.sleuthkit.autopsy.coreutils.Logger;
74import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
75import org.sleuthkit.autopsy.geolocation.datamodel.GeoLocationDataException;
76import org.sleuthkit.datamodel.TskCoreException;
77import javax.imageio.ImageIO;
78import javax.swing.SwingUtilities;
79import org.jxmapviewer.painter.CompoundPainter;
80import org.jxmapviewer.painter.Painter;
81import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
82
86@SuppressWarnings("deprecation")
87final public class MapPanel extends javax.swing.JPanel {
88
89 static final String CURRENT_MOUSE_GEOPOSITION = "CURRENT_MOUSE_GEOPOSITION";
90
91 private static final Logger logger = Logger.getLogger(MapPanel.class.getName());
92
93 private static final long serialVersionUID = 1L;
94 private static final Set<Integer> DOT_WAYPOINT_TYPES = new HashSet<>();
95 private static final int DOT_SIZE = 12;
96 private static final Set<Integer> VERY_SMALL_DOT_WAYPOINT_TYPES = new HashSet<>();
97 private static final int VERY_SMALL_DOT_SIZE = 6;
98
99 private boolean zoomChanging;
101 private Set<MapWaypoint> waypointSet;
102 private List<Set<MapWaypoint>> tracks = new ArrayList<>();
103 private List<Set<MapWaypoint>> areas = new ArrayList<>();
104
105 private Popup currentPopup;
106 private final PopupFactory popupFactory;
107
108 private static final int POPUP_WIDTH = 300;
109 private static final int POPUP_HEIGHT = 200;
110 private static final int POPUP_MARGIN = 10;
111
112 private BufferedImage whiteWaypointImage;
113 private BufferedImage transparentWaypointImage;
114
116 private Set<MapWaypoint> currentlySelectedSet;
117
118 static {
119 DOT_WAYPOINT_TYPES.add(ARTIFACT_TYPE.TSK_GPS_TRACKPOINT.getTypeID());
120 DOT_WAYPOINT_TYPES.add(ARTIFACT_TYPE.TSK_GPS_TRACK.getTypeID());
121 DOT_WAYPOINT_TYPES.add(ARTIFACT_TYPE.TSK_GPS_ROUTE.getTypeID());
122 VERY_SMALL_DOT_WAYPOINT_TYPES.add(ARTIFACT_TYPE.TSK_GPS_AREA.getTypeID());
123 }
124
128 @Messages({
129 "MapPanel_connection_failure_message=Failed to connect to new geolocation map tile source.",
130 "MapPanel_connection_failure_message_title=Connection Failure"
131 })
132 public MapPanel() {
134
135 zoomChanging = false;
136 currentPopup = null;
137 popupFactory = new PopupFactory();
138
139 try {
140 whiteWaypointImage = ImageIO.read(getClass().getResource("/org/sleuthkit/autopsy/images/waypoint_white.png"));
141 transparentWaypointImage = ImageIO.read(getClass().getResource("/org/sleuthkit/autopsy/images/waypoint_transparent.png"));
142 } catch (IOException ex) {
143 logger.log(Level.WARNING, "Unable to load geolocation waypoint images", ex);
144 }
145
146 // ComponentListeners do not have a concept of resize event "complete"
147 // therefore if we move the popup as the window resizes there will be
148 // a weird blinking behavior. Using the CompnentResizeEndListener the
149 // popup will move to its corner one the resize is completed.
150 this.addComponentListener(new ComponentResizeEndListener() {
151 @Override
152 public void resizeTimedOut() {
153 if(currentPopup != null) {
155 }
156 }
157 });
158
159 UserPreferences.addChangeListener(new PreferenceChangeListener() {
160 @Override
161 public void preferenceChange(PreferenceChangeEvent evt) {
162 try {
163 // Tell the old factory to cleanup
164 mapViewer.getTileFactory().dispose();
165
166 mapViewer.setTileFactory(getTileFactory());
167 initializeZoomSlider();
168 } catch (GeoLocationDataException ex) {
169 logger.log(Level.SEVERE, "Failed to connect to new geolocation tile server.", ex); //NON-NLS
170 JOptionPane.showMessageDialog(MapPanel.this,
171 Bundle.MapPanel_connection_failure_message(),
172 Bundle.MapPanel_connection_failure_message_title(),
173 JOptionPane.ERROR_MESSAGE);
175 Bundle.MapPanel_connection_failure_message_title(),
176 Bundle.MapPanel_connection_failure_message());
177 }
178 }
179 });
180 }
181
187 List<MapWaypoint> getVisibleWaypoints() {
188
189 Rectangle viewport = mapViewer.getViewportBounds();
190 List<MapWaypoint> waypoints = new ArrayList<>();
191
192 Iterator<MapWaypoint> iterator = waypointTree.iterator();
193 while (iterator.hasNext()) {
194 MapWaypoint waypoint = iterator.next();
195 if (viewport.contains(mapViewer.getTileFactory().geoToPixel(waypoint.getPosition(), mapViewer.getZoom()))) {
196 waypoints.add(waypoint);
197 }
198 }
199
200 return waypoints;
201 }
202
206 void initMap() throws GeoLocationDataException {
207
208 TileFactory tileFactory = getTileFactory();
209 mapViewer.setTileFactory(tileFactory);
210
211 // Add Mouse interactions
212 MouseInputListener mia = new MapPanMouseInputListener(mapViewer);
213 mapViewer.addMouseListener(mia);
214 mapViewer.addMouseMotionListener(mia);
215
216 mapViewer.addMouseListener(new CenterMapListener(mapViewer));
217 mapViewer.addMouseWheelListener(new ZoomMouseWheelListenerCursor(mapViewer));
218
219 // Listen to the map for a change in zoom so that we can update the slider.
220 mapViewer.addPropertyChangeListener("zoom", new PropertyChangeListener() {
221 @Override
222 public void propertyChange(PropertyChangeEvent evt) {
223 zoomSlider.setValue(mapViewer.getZoom());
224 }
225 });
226
227 zoomSlider.setMinimum(tileFactory.getInfo().getMinimumZoomLevel());
228 zoomSlider.setMaximum(tileFactory.getInfo().getMaximumZoomLevel());
229
230 setZoom(tileFactory.getInfo().getMaximumZoomLevel() - 1);
231
232 mapViewer.setCenterPosition(new GeoPosition(0, 0));
233
234 initializePainter();
235 }
236
237 void initializePainter() {
238 // Basic painters for the way points.
239 WaypointPainter<MapWaypoint> waypointPainter = new WaypointPainter<MapWaypoint>() {
240 @Override
241 public Set<MapWaypoint> getWaypoints() {
242 if (currentlySelectedWaypoint != null) {
243 waypointSet.remove(currentlySelectedWaypoint);
244 waypointSet.add(currentlySelectedWaypoint);
245 }
246 return waypointSet;
247 }
248 };
249 waypointPainter.setRenderer(new MapWaypointRenderer());
250
251 ArrayList<Painter<JXMapViewer>> painters = new ArrayList<>();
252 painters.add(new MapAreaRenderer(areas));
253 painters.add(new MapTrackRenderer(tracks));
254 painters.add(waypointPainter);
255
256 CompoundPainter<JXMapViewer> compoundPainter = new CompoundPainter<>(painters);
257 mapViewer.setOverlayPainter(compoundPainter);
258 }
259
263 void initializeZoomSlider() {
264 TileFactory tileFactory = mapViewer.getTileFactory();
265 zoomSlider.setMinimum(tileFactory.getInfo().getMinimumZoomLevel());
266 zoomSlider.setMaximum(tileFactory.getInfo().getMaximumZoomLevel());
267
268 zoomSlider.repaint();
269 zoomSlider.revalidate();
270 }
271
277 private TileFactory getTileFactory() throws GeoLocationDataException {
278 switch (GeolocationSettingsPanel.GeolocationDataSourceType.getOptionForValue(UserPreferences.getGeolocationtTileOption())) {
279 case ONLINE_USER_DEFINED_OSM_SERVER:
281 case OFFLINE_OSM_ZIP:
282 return new DefaultTileFactory(createOSMZipFactory(UserPreferences.getGeolocationOsmZipPath()));
283 case OFFILE_MBTILES_FILE:
284 return new MBTilesTileFactory(UserPreferences.getGeolocationMBTilesFilePath());
285 default:
286 return new DefaultTileFactory(new VirtualEarthTileFactoryInfo(VirtualEarthTileFactoryInfo.MAP));
287 }
288 }
289
299 private TileFactoryInfo createOnlineOSMFactory(String address) throws GeoLocationDataException {
300 if (address.isEmpty()) {
301 throw new GeoLocationDataException("Invalid user preference for OSM user define tile server. Address is an empty string.");
302 } else {
303 TileFactoryInfo info = new OSMTileFactoryInfo("User Defined Server", address);
304 return info;
305 }
306 }
307
317 private TileFactoryInfo createOSMZipFactory(String path) throws GeoLocationDataException {
318 if (path.isEmpty()) {
319 throw new GeoLocationDataException("Invalid OSM tile Zip file. User preference value is empty string.");
320 } else {
321 File file = new File(path);
322 if (!file.exists() || !file.canRead()) {
323 throw new GeoLocationDataException("Invalid OSM tile zip file. Unable to read file: " + path);
324 }
325
326 String zipPath = path.replaceAll("\\\\", "/");
327
328 return new OSMTileFactoryInfo("ZIP archive", "jar:file:/" + zipPath + "!"); //NON-NLS
329 }
330 }
331
337 void setWaypoints(Set<MapWaypoint> waypoints) {
338 waypointTree = new KdTree<>();
339 this.waypointSet = waypoints;
340 for (MapWaypoint waypoint : waypoints) {
341 waypointTree.add(waypoint);
342 }
343 mapViewer.repaint();
344 }
345
351 void setTracks(List<Set<MapWaypoint>> tracks) {
352 this.tracks = tracks;
353 }
354
360 void setAreas(List<Set<MapWaypoint>> areas) {
361 this.areas = areas;
362 }
363
369 void setZoom(int zoom) {
370 zoomChanging = true;
371 mapViewer.setZoom(zoom);
372 zoomSlider.setValue(zoom);
373 zoomChanging = false;
374 }
375
379 void clearWaypoints() {
380 waypointTree = null;
381 currentlySelectedWaypoint = null;
382 currentlySelectedSet = null;
383 if (currentPopup != null) {
384 currentPopup.hide();
385 }
386 mapViewer.repaint();
387 }
388
395 private void showPopupMenu(Point point) {
396 try {
397 List<MapWaypoint> waypoints = findClosestWaypoint(point);
398 MapWaypoint waypoint = null;
399 if (waypoints.size() > 0) {
400 waypoint = waypoints.get(0);
401 }
402 showPopupMenu(waypoint, point);
403 // Change the details popup to the currently selected point only if
404 // it the popup is currently visible
405 if (waypoint != null && !waypoint.equals(currentlySelectedWaypoint)) {
406 currentlySelectedWaypoint = waypoint;
407 if (currentPopup != null) {
409 }
410 mapViewer.repaint();
411 }
412 } catch (TskCoreException ex) {
413 logger.log(Level.WARNING, "Failed to show popup for waypoint", ex);
414 }
415 }
416
423 private void showPopupMenu(MapWaypoint waypoint, Point point) throws TskCoreException {
424 if (waypoint == null) {
425 return;
426 }
427
428 JMenuItem[] items = waypoint.getMenuItems();
429 JPopupMenu popupMenu = new JPopupMenu();
430 for (JMenuItem menu : items) {
431
432 if (menu != null) {
433 popupMenu.add(menu);
434 } else {
435 popupMenu.add(new JSeparator());
436 }
437 }
438 popupMenu.show(mapViewer, point.x, point.y);
439 }
440
444 private void showDetailsPopup() {
445 if (currentlySelectedWaypoint != null) {
446 if (currentPopup != null) {
447 currentPopup.hide();
448 }
449
450 WaypointDetailPanel detailPane = new WaypointDetailPanel();
451 detailPane.setWaypoint(currentlySelectedWaypoint);
452 detailPane.setPreferredSize(new Dimension(POPUP_WIDTH, POPUP_HEIGHT));
453 detailPane.addActionListener(new ActionListener() {
454 @Override
455 public void actionPerformed(ActionEvent e) {
456 if (currentPopup != null) {
457 currentPopup.hide();
458 currentPopup = null;
459 }
460 }
461
462 });
463
464 Point popupLocation = getLocationForDetailsPopup();
465
466 currentPopup = popupFactory.getPopup(this, detailPane, popupLocation.x, popupLocation.y);
467 currentPopup.show();
468
469 } else {
470 if (currentPopup != null) {
471 currentPopup.hide();
472 }
473 }
474
475 mapViewer.revalidate();
476 mapViewer.repaint();
477 }
478
485 Point panelCorner = this.getLocationOnScreen();
486 int width = this.getWidth();
487
488 int popupX = panelCorner.x + width - POPUP_WIDTH - POPUP_MARGIN;
489 int popupY = panelCorner.y + POPUP_MARGIN;
490
491 return new Point(popupX, popupY);
492 }
493
502 private List<MapWaypoint> findClosestWaypoint(Point clickPoint) {
503 if (waypointTree == null) {
504 return new ArrayList<>();
505 }
506
507 // Convert the mouse click location to latitude & longitude
508 GeoPosition geopos = mapViewer.convertPointToGeoPosition(clickPoint);
509
510 // Get the nearest neightbors to the point
511 Collection<MapWaypoint> waypoints = waypointTree.nearestNeighbourSearch(1, MapWaypoint.getDummyWaypoint(geopos));
512
513 if (waypoints == null || waypoints.isEmpty()) {
514 return null;
515 }
516
517 Iterator<MapWaypoint> iterator = waypoints.iterator();
518
519 // These may be the points closest to the lat/lon location that was
520 // clicked, but that doesn't mean they are close in terms of pixles.
521 List<MapWaypoint> closestPoints = new ArrayList<>();
522 while (iterator.hasNext()) {
523 MapWaypoint nextWaypoint = iterator.next();
524 Point2D point = mapViewer.convertGeoPositionToPoint(nextWaypoint.getPosition());
525 int pointX = (int) point.getX();
526 int pointY = (int) point.getY();
527 Rectangle rect;
528 if (DOT_WAYPOINT_TYPES.contains(nextWaypoint.getArtifactTypeID())) {
529 rect = new Rectangle(
530 pointX - (DOT_SIZE / 2),
531 pointY - (DOT_SIZE / 2),
532 DOT_SIZE,
534 );
535 } else if (VERY_SMALL_DOT_WAYPOINT_TYPES.contains(nextWaypoint.getArtifactTypeID())) {
536 rect = new Rectangle(
537 pointX - (VERY_SMALL_DOT_SIZE / 2),
538 pointY - (VERY_SMALL_DOT_SIZE / 2),
541 );
542 } else {
543 rect = new Rectangle(
544 pointX - (whiteWaypointImage.getWidth() / 2),
545 pointY - whiteWaypointImage.getHeight(),
546 whiteWaypointImage.getWidth(),
547 whiteWaypointImage.getHeight()
548 );
549 }
550
551 if (rect.contains(clickPoint)) {
552 closestPoints.add(nextWaypoint);
553 }
554 }
555
556 return closestPoints;
557 }
558
568 public abstract class ComponentResizeEndListener
569 extends ComponentAdapter
570 implements ActionListener {
571
572 private final Timer timer;
573 private static final int DEFAULT_TIMEOUT = 200;
574
580 this(DEFAULT_TIMEOUT);
581 }
582
588 public ComponentResizeEndListener(int delayMS) {
589 timer = new Timer(delayMS, this);
590 timer.setRepeats(false);
591 timer.setCoalesce(false);
592 }
593
594 @Override
595 public void componentResized(ComponentEvent e) {
596 timer.restart();
597 }
598
599 @Override
600 public void actionPerformed(ActionEvent e) {
602 }
603
607 public abstract void resizeTimedOut();
608 }
609
615 @SuppressWarnings("unchecked")
616 // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
617 private void initComponents() {
618 java.awt.GridBagConstraints gridBagConstraints;
619
620 mapViewer = new org.jxmapviewer.JXMapViewer();
621 zoomPanel = new javax.swing.JPanel();
622 zoomSlider = new javax.swing.JSlider();
623 javax.swing.JButton zoomInBtn = new javax.swing.JButton();
624 javax.swing.JButton zoomOutBtn = new javax.swing.JButton();
625
626 setFocusable(false);
627 setLayout(new java.awt.BorderLayout());
628
629 mapViewer.addMouseMotionListener(new java.awt.event.MouseMotionAdapter() {
630 public void mouseMoved(java.awt.event.MouseEvent evt) {
631 mapViewerMouseMoved(evt);
632 }
633 });
634 mapViewer.addMouseListener(new java.awt.event.MouseAdapter() {
635 public void mouseClicked(java.awt.event.MouseEvent evt) {
636 mapViewerMouseClicked(evt);
637 }
638 public void mousePressed(java.awt.event.MouseEvent evt) {
639 mapViewerMousePressed(evt);
640 }
641 public void mouseReleased(java.awt.event.MouseEvent evt) {
642 mapViewerMouseReleased(evt);
643 }
644 });
645 mapViewer.setLayout(new java.awt.GridBagLayout());
646
647 zoomPanel.setFocusable(false);
648 zoomPanel.setOpaque(false);
649 zoomPanel.setRequestFocusEnabled(false);
650 zoomPanel.setLayout(new java.awt.GridBagLayout());
651
652 zoomSlider.setMaximum(15);
653 zoomSlider.setMinimum(10);
654 zoomSlider.setMinorTickSpacing(1);
655 zoomSlider.setOrientation(javax.swing.JSlider.VERTICAL);
656 zoomSlider.setPaintTicks(true);
657 zoomSlider.setSnapToTicks(true);
658 zoomSlider.setInverted(true);
659 zoomSlider.setMinimumSize(new java.awt.Dimension(35, 100));
660 zoomSlider.setOpaque(false);
661 zoomSlider.setPreferredSize(new java.awt.Dimension(35, 190));
662 zoomSlider.addChangeListener(new javax.swing.event.ChangeListener() {
663 public void stateChanged(javax.swing.event.ChangeEvent evt) {
664 zoomSliderStateChanged(evt);
665 }
666 });
667 gridBagConstraints = new java.awt.GridBagConstraints();
668 gridBagConstraints.gridx = 0;
669 gridBagConstraints.gridy = 1;
670 zoomPanel.add(zoomSlider, gridBagConstraints);
671
672 zoomInBtn.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/plus-grey.png"))); // NOI18N
673 org.openide.awt.Mnemonics.setLocalizedText(zoomInBtn, org.openide.util.NbBundle.getMessage(MapPanel.class, "MapPanel.zoomInBtn.text")); // NOI18N
674 zoomInBtn.setBorder(null);
675 zoomInBtn.setBorderPainted(false);
676 zoomInBtn.setFocusPainted(false);
677 zoomInBtn.setRequestFocusEnabled(false);
678 zoomInBtn.setRolloverEnabled(false);
679 zoomInBtn.addActionListener(new java.awt.event.ActionListener() {
680 public void actionPerformed(java.awt.event.ActionEvent evt) {
681 zoomInBtnActionPerformed(evt);
682 }
683 });
684 gridBagConstraints = new java.awt.GridBagConstraints();
685 gridBagConstraints.gridx = 0;
686 gridBagConstraints.gridy = 0;
687 zoomPanel.add(zoomInBtn, gridBagConstraints);
688
689 zoomOutBtn.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/minus-grey.png"))); // NOI18N
690 org.openide.awt.Mnemonics.setLocalizedText(zoomOutBtn, org.openide.util.NbBundle.getMessage(MapPanel.class, "MapPanel.zoomOutBtn.text")); // NOI18N
691 zoomOutBtn.setBorder(null);
692 zoomOutBtn.setBorderPainted(false);
693 zoomOutBtn.setFocusPainted(false);
694 zoomOutBtn.setRequestFocusEnabled(false);
695 zoomOutBtn.setRolloverEnabled(false);
696 zoomOutBtn.addActionListener(new java.awt.event.ActionListener() {
697 public void actionPerformed(java.awt.event.ActionEvent evt) {
698 zoomOutBtnActionPerformed(evt);
699 }
700 });
701 gridBagConstraints = new java.awt.GridBagConstraints();
702 gridBagConstraints.gridx = 0;
703 gridBagConstraints.gridy = 2;
704 zoomPanel.add(zoomOutBtn, gridBagConstraints);
705
706 gridBagConstraints = new java.awt.GridBagConstraints();
707 gridBagConstraints.anchor = java.awt.GridBagConstraints.SOUTHWEST;
708 gridBagConstraints.weightx = 1.0;
709 gridBagConstraints.weighty = 1.0;
710 gridBagConstraints.insets = new java.awt.Insets(4, 4, 4, 4);
711 mapViewer.add(zoomPanel, gridBagConstraints);
712
713 add(mapViewer, java.awt.BorderLayout.CENTER);
714 }// </editor-fold>//GEN-END:initComponents
715
716 private void zoomSliderStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_zoomSliderStateChanged
717 if (!zoomChanging) {
718 setZoom(zoomSlider.getValue());
719 }
720 }//GEN-LAST:event_zoomSliderStateChanged
721
722 private void mapViewerMousePressed(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_mapViewerMousePressed
723 if (evt.isPopupTrigger()) {
724 showPopupMenu(evt.getPoint());
725 }
726 }//GEN-LAST:event_mapViewerMousePressed
727
728 private void mapViewerMouseReleased(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_mapViewerMouseReleased
729 if (evt.isPopupTrigger()) {
730 showPopupMenu(evt.getPoint());
731 }
732 }//GEN-LAST:event_mapViewerMouseReleased
733
734 private void mapViewerMouseMoved(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_mapViewerMouseMoved
735 GeoPosition geopos = mapViewer.convertPointToGeoPosition(evt.getPoint());
736 firePropertyChange(CURRENT_MOUSE_GEOPOSITION, null, geopos);
737 }//GEN-LAST:event_mapViewerMouseMoved
738
739 private void mapViewerMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_mapViewerMouseClicked
740 if (!evt.isPopupTrigger() && SwingUtilities.isLeftMouseButton(evt)) {
741 List<MapWaypoint> waypoints = findClosestWaypoint(evt.getPoint());
742 if (waypoints.size() > 0) {
743 MapWaypoint selection = waypoints.get(0);
744 currentlySelectedWaypoint = selection;
746 for (Set<MapWaypoint> track : tracks) {
747 if (track.contains(selection)) {
748 currentlySelectedSet = track;
749 break;
750 }
751 }
752 if (currentlySelectedSet == null) {
753 for (Set<MapWaypoint> area : areas) {
754 if (area.contains(selection)) {
756 break;
757 }
758 }
759 }
760 } else {
763 }
765 }
766 }//GEN-LAST:event_mapViewerMouseClicked
767
768 private void zoomInBtnActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_zoomInBtnActionPerformed
769 int currentValue = mapViewer.getZoom();
770 setZoom(currentValue - 1);
771 }//GEN-LAST:event_zoomInBtnActionPerformed
772
773 private void zoomOutBtnActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_zoomOutBtnActionPerformed
774 int currentValue = mapViewer.getZoom();
775 setZoom(currentValue + 1);
776 }//GEN-LAST:event_zoomOutBtnActionPerformed
777
778
779 // Variables declaration - do not modify//GEN-BEGIN:variables
780 private org.jxmapviewer.JXMapViewer mapViewer;
781 private javax.swing.JPanel zoomPanel;
782 private javax.swing.JSlider zoomSlider;
783 // End of variables declaration//GEN-END:variables
784
788 private class MapWaypointRenderer implements WaypointRenderer<MapWaypoint> {
789
790 private final Map<Color, BufferedImage> dotImageCache = new HashMap<>();
791 private final Map<Color, BufferedImage> verySmallDotImageCache = new HashMap<>();
792 private final Map<Color, BufferedImage> waypointImageCache = new HashMap<>();
793
800 private Color getColor(MapWaypoint waypoint) {
801 Color baseColor = waypoint.getColor();
803 || (currentlySelectedSet != null && currentlySelectedSet.contains(waypoint))) {
804 // Highlight this waypoint since it is selected
805 return Color.YELLOW;
806 } else {
807 return baseColor;
808 }
809 }
810
819 private BufferedImage createTrackDotImage(Color color, int s) {
820
821 BufferedImage ret = new BufferedImage(s, s, BufferedImage.TYPE_INT_ARGB);
822 Graphics2D g = ret.createGraphics();
823 g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
824 g.setColor(color);
825 g.fillOval(1, 1, s - 2, s - 2);
826 g.setColor(Color.BLACK);
827 g.setStroke(new BasicStroke(1));
828 g.drawOval(1, 1, s - 2, s - 2);
829 g.dispose();
830 return ret;
831 }
832
840 private BufferedImage createWaypointImage(Color color) {
841 int w = whiteWaypointImage.getWidth();
842 int h = whiteWaypointImage.getHeight();
843
844 BufferedImage ret = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
845
846 Graphics2D g = ret.createGraphics();
847 g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
848 g.drawImage(whiteWaypointImage, 0, 0, null);
849 g.setComposite(AlphaComposite.SrcIn);
850 g.setColor(color);
851 g.fillRect(0, 0, w, h);
852 g.setComposite(AlphaComposite.SrcAtop);
853 g.drawImage(transparentWaypointImage, 0, 0, null);
854 g.dispose();
855 return ret;
856 }
857
858 @Override
859 public void paintWaypoint(Graphics2D g, JXMapViewer map, MapWaypoint waypoint) {
860 Color color = getColor(waypoint);
861 BufferedImage image;
862 Point2D point = map.getTileFactory().geoToPixel(waypoint.getPosition(), map.getZoom());
863 int x = (int) point.getX();
864 int y = (int) point.getY();
865
866 if (DOT_WAYPOINT_TYPES.contains(waypoint.getArtifactTypeID())) {
867 image = dotImageCache.computeIfAbsent(color, k -> {
868 return createTrackDotImage(color, DOT_SIZE);
869 });
870 // Center the dot on the GPS coordinate
871 y -= image.getHeight() / 2;
872 } else if (VERY_SMALL_DOT_WAYPOINT_TYPES.contains(waypoint.getArtifactTypeID())) {
873 image = verySmallDotImageCache.computeIfAbsent(color, k -> {
875 });
876 // Center the dot on the GPS coordinate
877 y -= image.getHeight() / 2;
878 } else {
879 image = waypointImageCache.computeIfAbsent(color, k -> {
880 return createWaypointImage(color);
881 });
882 // Align the bottom of the pin with the GPS coordinate
883 y -= image.getHeight();
884 }
885 // Center image horizontally on image
886 x -= image.getWidth() / 2;
887
888 Graphics2D g2d = (Graphics2D) g.create();
889 g2d.drawImage(image, x, y, null);
890 g2d.dispose();
891 }
892 }
893
897 private class MapTrackRenderer implements Painter<JXMapViewer> {
898
899 private final List<Set<MapWaypoint>> tracks;
900
901 MapTrackRenderer(List<Set<MapWaypoint>> tracks) {
902 this.tracks = tracks;
903 }
904
905 private void drawRoute(Set<MapWaypoint> track, Graphics2D g, JXMapViewer map) {
906 int lastX = 0;
907 int lastY = 0;
908
909 boolean first = true;
910
911 for (MapWaypoint wp : track) {
912 Point2D p = map.getTileFactory().geoToPixel(wp.getPosition(), map.getZoom());
913 int thisX = (int) p.getX();
914 int thisY = (int) p.getY();
915
916 if (first) {
917 first = false;
918 } else {
919 g.drawLine(lastX, lastY, thisX, thisY);
920 }
921
922 lastX = thisX;
923 lastY = thisY;
924 }
925 }
926
927 @Override
928 public void paint(Graphics2D g, JXMapViewer map, int w, int h) {
929 Graphics2D g2d = (Graphics2D) g.create();
930
931 Rectangle bounds = map.getViewportBounds();
932 g2d.translate(-bounds.x, -bounds.y);
933
934 g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
935
936 g2d.setColor(Color.BLACK);
937 g2d.setStroke(new BasicStroke(2));
938
939 for (Set<MapWaypoint> track : tracks) {
940 drawRoute(track, g2d, map);
941 }
942
943 g2d.dispose();
944 }
945 }
946
950 private class MapAreaRenderer implements Painter<JXMapViewer> {
951
952 private final List<Set<MapWaypoint>> areas;
953
954 MapAreaRenderer(List<Set<MapWaypoint>> areas) {
955 this.areas = areas;
956 }
957
965 private void drawArea(Set<MapWaypoint> area, Graphics2D g, JXMapViewer map) {
966 if (area.isEmpty()) {
967 return;
968 }
969 boolean first = true;
970
971 GeneralPath polygon = new GeneralPath(GeneralPath.WIND_EVEN_ODD, area.size());
972
973 for (MapWaypoint wp : area) {
974 Point2D p = map.getTileFactory().geoToPixel(wp.getPosition(), map.getZoom());
975 int thisX = (int) p.getX();
976 int thisY = (int) p.getY();
977
978 if (first) {
979 polygon.moveTo(thisX, thisY);
980 first = false;
981 } else {
982 polygon.lineTo(thisX, thisY);
983 }
984 }
985 polygon.closePath();
986
987 Color areaColor = area.iterator().next().getColor();
988 final double maxColorValue = 255.0;
989 g.setPaint(new Color((float)(areaColor.getRed() / maxColorValue),
990 (float)(areaColor.getGreen() / maxColorValue),
991 (float)(areaColor.getBlue() / maxColorValue),
992 .2f));
993 g.fill(polygon);
994 g.draw(polygon);
995 }
996
997 @Override
998 public void paint(Graphics2D g, JXMapViewer map, int w, int h) {
999 Graphics2D g2d = (Graphics2D) g.create();
1000
1001 Rectangle bounds = map.getViewportBounds();
1002 g2d.translate(-bounds.x, -bounds.y);
1003
1004 g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
1005
1006 g2d.setColor(Color.BLACK);
1007 g2d.setStroke(new BasicStroke(2));
1008
1009 for (Set<MapWaypoint> area : areas) {
1010 drawArea(area, g2d, map);
1011 }
1012
1013 g2d.dispose();
1014 }
1015 }
1016}
static void addChangeListener(PreferenceChangeListener listener)
synchronized static Logger getLogger(String name)
Definition Logger.java:124
void drawArea(Set< MapWaypoint > area, Graphics2D g, JXMapViewer map)
void paint(Graphics2D g, JXMapViewer map, int w, int h)
void drawRoute(Set< MapWaypoint > track, Graphics2D g, JXMapViewer map)
void paint(Graphics2D g, JXMapViewer map, int w, int h)
void paintWaypoint(Graphics2D g, JXMapViewer map, MapWaypoint waypoint)
void mapViewerMousePressed(java.awt.event.MouseEvent evt)
TileFactoryInfo createOnlineOSMFactory(String address)
org.jxmapviewer.JXMapViewer mapViewer
List< MapWaypoint > findClosestWaypoint(Point clickPoint)
void mapViewerMouseClicked(java.awt.event.MouseEvent evt)
void zoomOutBtnActionPerformed(java.awt.event.ActionEvent evt)
static final Set< Integer > VERY_SMALL_DOT_WAYPOINT_TYPES
Definition MapPanel.java:96
void zoomSliderStateChanged(javax.swing.event.ChangeEvent evt)
void mapViewerMouseReleased(java.awt.event.MouseEvent evt)
List< Set< MapWaypoint > > tracks
void showPopupMenu(MapWaypoint waypoint, Point point)
static final Set< Integer > DOT_WAYPOINT_TYPES
Definition MapPanel.java:94
TileFactoryInfo createOSMZipFactory(String path)
void zoomInBtnActionPerformed(java.awt.event.ActionEvent evt)
void mapViewerMouseMoved(java.awt.event.MouseEvent evt)

Copyright © 2012-2024 Sleuth Kit Labs. Generated on:
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.