Autopsy  4.19.3
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  */
19 package org.sleuthkit.autopsy.geolocation;
20 
21 import java.awt.AlphaComposite;
22 import java.awt.BasicStroke;
23 import java.awt.Color;
24 import java.awt.Dimension;
25 import java.awt.Graphics2D;
26 import java.awt.Paint;
27 import java.awt.Point;
28 import java.awt.Rectangle;
29 import java.awt.RenderingHints;
30 import java.awt.event.ActionEvent;
31 import java.awt.event.ActionListener;
32 import java.awt.event.ComponentAdapter;
33 import java.awt.event.ComponentEvent;
34 import java.awt.geom.GeneralPath;
35 import java.awt.geom.Point2D;
36 import java.awt.image.BufferedImage;
37 import java.beans.PropertyChangeEvent;
38 import java.beans.PropertyChangeListener;
39 import java.io.File;
40 import java.io.IOException;
41 import java.util.ArrayList;
42 import java.util.Collection;
43 import java.util.HashMap;
44 import java.util.HashSet;
45 import java.util.Iterator;
46 import java.util.List;
47 import java.util.Map;
48 import java.util.Set;
49 import java.util.logging.Level;
50 import java.util.prefs.PreferenceChangeEvent;
51 import java.util.prefs.PreferenceChangeListener;
52 import javax.swing.JMenuItem;
53 import javax.swing.JOptionPane;
54 import javax.swing.JPopupMenu;
55 import javax.swing.JSeparator;
56 import javax.swing.Popup;
57 import javax.swing.PopupFactory;
58 import javax.swing.Timer;
59 import javax.swing.event.MouseInputListener;
60 import org.jxmapviewer.JXMapViewer;
61 import org.jxmapviewer.OSMTileFactoryInfo;
62 import org.jxmapviewer.VirtualEarthTileFactoryInfo;
63 import org.jxmapviewer.input.CenterMapListener;
64 import org.jxmapviewer.input.ZoomMouseWheelListenerCursor;
65 import org.jxmapviewer.viewer.DefaultTileFactory;
66 import org.jxmapviewer.viewer.GeoPosition;
67 import org.jxmapviewer.viewer.TileFactory;
68 import org.jxmapviewer.viewer.TileFactoryInfo;
69 import org.jxmapviewer.viewer.WaypointPainter;
70 import org.jxmapviewer.viewer.WaypointRenderer;
71 import org.openide.util.NbBundle.Messages;
76 import org.sleuthkit.datamodel.TskCoreException;
77 import javax.imageio.ImageIO;
78 import javax.swing.SwingUtilities;
79 import org.jxmapviewer.painter.CompoundPainter;
80 import org.jxmapviewer.painter.Painter;
81 import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
82 
86 @SuppressWarnings("deprecation")
87 final 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() {
133  initComponents();
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) {
154  showDetailsPopup();
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:
280  return new DefaultTileFactory(createOnlineOSMFactory(UserPreferences.getGeolocationOsmServerAddress()));
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) {
408  showDetailsPopup();
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 
484  private Point getLocationForDetailsPopup() {
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,
533  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),
539  VERY_SMALL_DOT_SIZE,
540  VERY_SMALL_DOT_SIZE
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) {
601  resizeTimedOut();
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;
745  currentlySelectedSet = null;
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)) {
755  currentlySelectedSet = area;
756  break;
757  }
758  }
759  }
760  } else {
761  currentlySelectedWaypoint = null;
762  currentlySelectedSet = null;
763  }
764  showDetailsPopup();
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();
802  if (waypoint.equals(currentlySelectedWaypoint)
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 -> {
874  return createTrackDotImage(color, VERY_SMALL_DOT_SIZE);
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 }
void paint(Graphics2D g, JXMapViewer map, int w, int h)
Definition: MapPanel.java:998
void zoomInBtnActionPerformed(java.awt.event.ActionEvent evt)
Definition: MapPanel.java:768
void showPopupMenu(MapWaypoint waypoint, Point point)
Definition: MapPanel.java:423
void zoomSliderStateChanged(javax.swing.event.ChangeEvent evt)
Definition: MapPanel.java:716
void mapViewerMouseReleased(java.awt.event.MouseEvent evt)
Definition: MapPanel.java:728
Collection< T > nearestNeighbourSearch(int numNeighbors, T value)
Definition: KdTree.java:256
TileFactoryInfo createOnlineOSMFactory(String address)
Definition: MapPanel.java:299
void mapViewerMouseClicked(java.awt.event.MouseEvent evt)
Definition: MapPanel.java:739
void mapViewerMousePressed(java.awt.event.MouseEvent evt)
Definition: MapPanel.java:722
void mapViewerMouseMoved(java.awt.event.MouseEvent evt)
Definition: MapPanel.java:734
void drawRoute(Set< MapWaypoint > track, Graphics2D g, JXMapViewer map)
Definition: MapPanel.java:905
List< MapWaypoint > findClosestWaypoint(Point clickPoint)
Definition: MapPanel.java:502
void drawArea(Set< MapWaypoint > area, Graphics2D g, JXMapViewer map)
Definition: MapPanel.java:965
TileFactoryInfo createOSMZipFactory(String path)
Definition: MapPanel.java:317
void paintWaypoint(Graphics2D g, JXMapViewer map, MapWaypoint waypoint)
Definition: MapPanel.java:859
static void error(String title, String message)
synchronized static Logger getLogger(String name)
Definition: Logger.java:124
void zoomOutBtnActionPerformed(java.awt.event.ActionEvent evt)
Definition: MapPanel.java:773
static void addChangeListener(PreferenceChangeListener listener)
void paint(Graphics2D g, JXMapViewer map, int w, int h)
Definition: MapPanel.java:928
org.jxmapviewer.JXMapViewer mapViewer
Definition: MapPanel.java:780

Copyright © 2012-2022 Basis Technology. Generated on: Tue Jun 27 2023
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.