Autopsy  4.19.0
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  showDetailsPopup();
154  }
155  });
156 
157  UserPreferences.addChangeListener(new PreferenceChangeListener() {
158  @Override
159  public void preferenceChange(PreferenceChangeEvent evt) {
160  try {
161  // Tell the old factory to cleanup
162  mapViewer.getTileFactory().dispose();
163 
164  mapViewer.setTileFactory(getTileFactory());
165  initializeZoomSlider();
166  } catch (GeoLocationDataException ex) {
167  logger.log(Level.SEVERE, "Failed to connect to new geolocation tile server.", ex); //NON-NLS
168  JOptionPane.showMessageDialog(MapPanel.this,
169  Bundle.MapPanel_connection_failure_message(),
170  Bundle.MapPanel_connection_failure_message_title(),
171  JOptionPane.ERROR_MESSAGE);
173  Bundle.MapPanel_connection_failure_message_title(),
174  Bundle.MapPanel_connection_failure_message());
175  }
176  }
177  });
178  }
179 
185  List<MapWaypoint> getVisibleWaypoints() {
186 
187  Rectangle viewport = mapViewer.getViewportBounds();
188  List<MapWaypoint> waypoints = new ArrayList<>();
189 
190  Iterator<MapWaypoint> iterator = waypointTree.iterator();
191  while (iterator.hasNext()) {
192  MapWaypoint waypoint = iterator.next();
193  if (viewport.contains(mapViewer.getTileFactory().geoToPixel(waypoint.getPosition(), mapViewer.getZoom()))) {
194  waypoints.add(waypoint);
195  }
196  }
197 
198  return waypoints;
199  }
200 
204  void initMap() throws GeoLocationDataException {
205 
206  TileFactory tileFactory = getTileFactory();
207  mapViewer.setTileFactory(tileFactory);
208 
209  // Add Mouse interactions
210  MouseInputListener mia = new MapPanMouseInputListener(mapViewer);
211  mapViewer.addMouseListener(mia);
212  mapViewer.addMouseMotionListener(mia);
213 
214  mapViewer.addMouseListener(new CenterMapListener(mapViewer));
215  mapViewer.addMouseWheelListener(new ZoomMouseWheelListenerCursor(mapViewer));
216 
217  // Listen to the map for a change in zoom so that we can update the slider.
218  mapViewer.addPropertyChangeListener("zoom", new PropertyChangeListener() {
219  @Override
220  public void propertyChange(PropertyChangeEvent evt) {
221  zoomSlider.setValue(mapViewer.getZoom());
222  }
223  });
224 
225  zoomSlider.setMinimum(tileFactory.getInfo().getMinimumZoomLevel());
226  zoomSlider.setMaximum(tileFactory.getInfo().getMaximumZoomLevel());
227 
228  setZoom(tileFactory.getInfo().getMaximumZoomLevel() - 1);
229 
230  mapViewer.setCenterPosition(new GeoPosition(0, 0));
231 
232  initializePainter();
233  }
234 
235  void initializePainter() {
236  // Basic painters for the way points.
237  WaypointPainter<MapWaypoint> waypointPainter = new WaypointPainter<MapWaypoint>() {
238  @Override
239  public Set<MapWaypoint> getWaypoints() {
240  if (currentlySelectedWaypoint != null) {
241  waypointSet.remove(currentlySelectedWaypoint);
242  waypointSet.add(currentlySelectedWaypoint);
243  }
244  return waypointSet;
245  }
246  };
247  waypointPainter.setRenderer(new MapWaypointRenderer());
248 
249  ArrayList<Painter<JXMapViewer>> painters = new ArrayList<>();
250  painters.add(new MapAreaRenderer(areas));
251  painters.add(new MapTrackRenderer(tracks));
252  painters.add(waypointPainter);
253 
254  CompoundPainter<JXMapViewer> compoundPainter = new CompoundPainter<>(painters);
255  mapViewer.setOverlayPainter(compoundPainter);
256  }
257 
261  void initializeZoomSlider() {
262  TileFactory tileFactory = mapViewer.getTileFactory();
263  zoomSlider.setMinimum(tileFactory.getInfo().getMinimumZoomLevel());
264  zoomSlider.setMaximum(tileFactory.getInfo().getMaximumZoomLevel());
265 
266  zoomSlider.repaint();
267  zoomSlider.revalidate();
268  }
269 
275  private TileFactory getTileFactory() throws GeoLocationDataException {
276  switch (GeolocationSettingsPanel.GeolocationDataSourceType.getOptionForValue(UserPreferences.getGeolocationtTileOption())) {
277  case ONLINE_USER_DEFINED_OSM_SERVER:
278  return new DefaultTileFactory(createOnlineOSMFactory(UserPreferences.getGeolocationOsmServerAddress()));
279  case OFFLINE_OSM_ZIP:
280  return new DefaultTileFactory(createOSMZipFactory(UserPreferences.getGeolocationOsmZipPath()));
281  case OFFILE_MBTILES_FILE:
282  return new MBTilesTileFactory(UserPreferences.getGeolocationMBTilesFilePath());
283  default:
284  return new DefaultTileFactory(new VirtualEarthTileFactoryInfo(VirtualEarthTileFactoryInfo.MAP));
285  }
286  }
287 
297  private TileFactoryInfo createOnlineOSMFactory(String address) throws GeoLocationDataException {
298  if (address.isEmpty()) {
299  throw new GeoLocationDataException("Invalid user preference for OSM user define tile server. Address is an empty string.");
300  } else {
301  TileFactoryInfo info = new OSMTileFactoryInfo("User Defined Server", address);
302  return info;
303  }
304  }
305 
315  private TileFactoryInfo createOSMZipFactory(String path) throws GeoLocationDataException {
316  if (path.isEmpty()) {
317  throw new GeoLocationDataException("Invalid OSM tile Zip file. User preference value is empty string.");
318  } else {
319  File file = new File(path);
320  if (!file.exists() || !file.canRead()) {
321  throw new GeoLocationDataException("Invalid OSM tile zip file. Unable to read file: " + path);
322  }
323 
324  String zipPath = path.replaceAll("\\\\", "/");
325 
326  return new OSMTileFactoryInfo("ZIP archive", "jar:file:/" + zipPath + "!"); //NON-NLS
327  }
328  }
329 
335  void setWaypoints(Set<MapWaypoint> waypoints) {
336  waypointTree = new KdTree<>();
337  this.waypointSet = waypoints;
338  for (MapWaypoint waypoint : waypoints) {
339  waypointTree.add(waypoint);
340  }
341  mapViewer.repaint();
342  }
343 
349  void setTracks(List<Set<MapWaypoint>> tracks) {
350  this.tracks = tracks;
351  }
352 
358  void setAreas(List<Set<MapWaypoint>> areas) {
359  this.areas = areas;
360  }
361 
367  void setZoom(int zoom) {
368  zoomChanging = true;
369  mapViewer.setZoom(zoom);
370  zoomSlider.setValue(zoom);
371  zoomChanging = false;
372  }
373 
377  void clearWaypoints() {
378  waypointTree = null;
379  currentlySelectedWaypoint = null;
380  currentlySelectedSet = null;
381  if (currentPopup != null) {
382  currentPopup.hide();
383  }
384  mapViewer.repaint();
385  }
386 
393  private void showPopupMenu(Point point) {
394  try {
395  List<MapWaypoint> waypoints = findClosestWaypoint(point);
396  MapWaypoint waypoint = null;
397  if (waypoints.size() > 0) {
398  waypoint = waypoints.get(0);
399  }
400  showPopupMenu(waypoint, point);
401  // Change the details popup to the currently selected point only if
402  // it the popup is currently visible
403  if (waypoint != null && !waypoint.equals(currentlySelectedWaypoint)) {
404  currentlySelectedWaypoint = waypoint;
405  if (currentPopup != null) {
406  showDetailsPopup();
407  }
408  mapViewer.repaint();
409  }
410  } catch (TskCoreException ex) {
411  logger.log(Level.WARNING, "Failed to show popup for waypoint", ex);
412  }
413  }
414 
421  private void showPopupMenu(MapWaypoint waypoint, Point point) throws TskCoreException {
422  if (waypoint == null) {
423  return;
424  }
425 
426  JMenuItem[] items = waypoint.getMenuItems();
427  JPopupMenu popupMenu = new JPopupMenu();
428  for (JMenuItem menu : items) {
429 
430  if (menu != null) {
431  popupMenu.add(menu);
432  } else {
433  popupMenu.add(new JSeparator());
434  }
435  }
436  popupMenu.show(mapViewer, point.x, point.y);
437  }
438 
442  private void showDetailsPopup() {
443  if (currentlySelectedWaypoint != null) {
444  if (currentPopup != null) {
445  currentPopup.hide();
446  }
447 
448  WaypointDetailPanel detailPane = new WaypointDetailPanel();
449  detailPane.setWaypoint(currentlySelectedWaypoint);
450  detailPane.setPreferredSize(new Dimension(POPUP_WIDTH, POPUP_HEIGHT));
451  detailPane.addActionListener(new ActionListener() {
452  @Override
453  public void actionPerformed(ActionEvent e) {
454  if (currentPopup != null) {
455  currentPopup.hide();
456  currentPopup = null;
457  }
458  }
459 
460  });
461 
462  Point popupLocation = getLocationForDetailsPopup();
463 
464  currentPopup = popupFactory.getPopup(this, detailPane, popupLocation.x, popupLocation.y);
465  currentPopup.show();
466 
467  } else {
468  if (currentPopup != null) {
469  currentPopup.hide();
470  }
471  }
472 
473  mapViewer.revalidate();
474  mapViewer.repaint();
475  }
476 
482  private Point getLocationForDetailsPopup() {
483  Point panelCorner = this.getLocationOnScreen();
484  int width = this.getWidth();
485 
486  int popupX = panelCorner.x + width - POPUP_WIDTH - POPUP_MARGIN;
487  int popupY = panelCorner.y + POPUP_MARGIN;
488 
489  return new Point(popupX, popupY);
490  }
491 
500  private List<MapWaypoint> findClosestWaypoint(Point clickPoint) {
501  if (waypointTree == null) {
502  return new ArrayList<>();
503  }
504 
505  // Convert the mouse click location to latitude & longitude
506  GeoPosition geopos = mapViewer.convertPointToGeoPosition(clickPoint);
507 
508  // Get the nearest neightbors to the point
509  Collection<MapWaypoint> waypoints = waypointTree.nearestNeighbourSearch(1, MapWaypoint.getDummyWaypoint(geopos));
510 
511  if (waypoints == null || waypoints.isEmpty()) {
512  return null;
513  }
514 
515  Iterator<MapWaypoint> iterator = waypoints.iterator();
516 
517  // These may be the points closest to the lat/lon location that was
518  // clicked, but that doesn't mean they are close in terms of pixles.
519  List<MapWaypoint> closestPoints = new ArrayList<>();
520  while (iterator.hasNext()) {
521  MapWaypoint nextWaypoint = iterator.next();
522  Point2D point = mapViewer.convertGeoPositionToPoint(nextWaypoint.getPosition());
523  int pointX = (int) point.getX();
524  int pointY = (int) point.getY();
525  Rectangle rect;
526  if (DOT_WAYPOINT_TYPES.contains(nextWaypoint.getArtifactTypeID())) {
527  rect = new Rectangle(
528  pointX - (DOT_SIZE / 2),
529  pointY - (DOT_SIZE / 2),
530  DOT_SIZE,
531  DOT_SIZE
532  );
533  } else if (VERY_SMALL_DOT_WAYPOINT_TYPES.contains(nextWaypoint.getArtifactTypeID())) {
534  rect = new Rectangle(
535  pointX - (VERY_SMALL_DOT_SIZE / 2),
536  pointY - (VERY_SMALL_DOT_SIZE / 2),
537  VERY_SMALL_DOT_SIZE,
538  VERY_SMALL_DOT_SIZE
539  );
540  } else {
541  rect = new Rectangle(
542  pointX - (whiteWaypointImage.getWidth() / 2),
543  pointY - whiteWaypointImage.getHeight(),
544  whiteWaypointImage.getWidth(),
545  whiteWaypointImage.getHeight()
546  );
547  }
548 
549  if (rect.contains(clickPoint)) {
550  closestPoints.add(nextWaypoint);
551  }
552  }
553 
554  return closestPoints;
555  }
556 
566  public abstract class ComponentResizeEndListener
567  extends ComponentAdapter
568  implements ActionListener {
569 
570  private final Timer timer;
571  private static final int DEFAULT_TIMEOUT = 200;
572 
578  this(DEFAULT_TIMEOUT);
579  }
580 
586  public ComponentResizeEndListener(int delayMS) {
587  timer = new Timer(delayMS, this);
588  timer.setRepeats(false);
589  timer.setCoalesce(false);
590  }
591 
592  @Override
593  public void componentResized(ComponentEvent e) {
594  timer.restart();
595  }
596 
597  @Override
598  public void actionPerformed(ActionEvent e) {
599  resizeTimedOut();
600  }
601 
605  public abstract void resizeTimedOut();
606  }
607 
613  @SuppressWarnings("unchecked")
614  // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
615  private void initComponents() {
616  java.awt.GridBagConstraints gridBagConstraints;
617 
618  mapViewer = new org.jxmapviewer.JXMapViewer();
619  zoomPanel = new javax.swing.JPanel();
620  zoomSlider = new javax.swing.JSlider();
621  javax.swing.JButton zoomInBtn = new javax.swing.JButton();
622  javax.swing.JButton zoomOutBtn = new javax.swing.JButton();
623 
624  setFocusable(false);
625  setLayout(new java.awt.BorderLayout());
626 
627  mapViewer.addMouseMotionListener(new java.awt.event.MouseMotionAdapter() {
628  public void mouseMoved(java.awt.event.MouseEvent evt) {
629  mapViewerMouseMoved(evt);
630  }
631  });
632  mapViewer.addMouseListener(new java.awt.event.MouseAdapter() {
633  public void mouseClicked(java.awt.event.MouseEvent evt) {
634  mapViewerMouseClicked(evt);
635  }
636  public void mousePressed(java.awt.event.MouseEvent evt) {
637  mapViewerMousePressed(evt);
638  }
639  public void mouseReleased(java.awt.event.MouseEvent evt) {
640  mapViewerMouseReleased(evt);
641  }
642  });
643  mapViewer.setLayout(new java.awt.GridBagLayout());
644 
645  zoomPanel.setFocusable(false);
646  zoomPanel.setOpaque(false);
647  zoomPanel.setRequestFocusEnabled(false);
648  zoomPanel.setLayout(new java.awt.GridBagLayout());
649 
650  zoomSlider.setMaximum(15);
651  zoomSlider.setMinimum(10);
652  zoomSlider.setMinorTickSpacing(1);
653  zoomSlider.setOrientation(javax.swing.JSlider.VERTICAL);
654  zoomSlider.setPaintTicks(true);
655  zoomSlider.setSnapToTicks(true);
656  zoomSlider.setInverted(true);
657  zoomSlider.setMinimumSize(new java.awt.Dimension(35, 100));
658  zoomSlider.setOpaque(false);
659  zoomSlider.setPreferredSize(new java.awt.Dimension(35, 190));
660  zoomSlider.addChangeListener(new javax.swing.event.ChangeListener() {
661  public void stateChanged(javax.swing.event.ChangeEvent evt) {
662  zoomSliderStateChanged(evt);
663  }
664  });
665  gridBagConstraints = new java.awt.GridBagConstraints();
666  gridBagConstraints.gridx = 0;
667  gridBagConstraints.gridy = 1;
668  zoomPanel.add(zoomSlider, gridBagConstraints);
669 
670  zoomInBtn.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/plus-grey.png"))); // NOI18N
671  org.openide.awt.Mnemonics.setLocalizedText(zoomInBtn, org.openide.util.NbBundle.getMessage(MapPanel.class, "MapPanel.zoomInBtn.text")); // NOI18N
672  zoomInBtn.setBorder(null);
673  zoomInBtn.setBorderPainted(false);
674  zoomInBtn.setFocusPainted(false);
675  zoomInBtn.setRequestFocusEnabled(false);
676  zoomInBtn.setRolloverEnabled(false);
677  zoomInBtn.addActionListener(new java.awt.event.ActionListener() {
678  public void actionPerformed(java.awt.event.ActionEvent evt) {
679  zoomInBtnActionPerformed(evt);
680  }
681  });
682  gridBagConstraints = new java.awt.GridBagConstraints();
683  gridBagConstraints.gridx = 0;
684  gridBagConstraints.gridy = 0;
685  zoomPanel.add(zoomInBtn, gridBagConstraints);
686 
687  zoomOutBtn.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/minus-grey.png"))); // NOI18N
688  org.openide.awt.Mnemonics.setLocalizedText(zoomOutBtn, org.openide.util.NbBundle.getMessage(MapPanel.class, "MapPanel.zoomOutBtn.text")); // NOI18N
689  zoomOutBtn.setBorder(null);
690  zoomOutBtn.setBorderPainted(false);
691  zoomOutBtn.setFocusPainted(false);
692  zoomOutBtn.setRequestFocusEnabled(false);
693  zoomOutBtn.setRolloverEnabled(false);
694  zoomOutBtn.addActionListener(new java.awt.event.ActionListener() {
695  public void actionPerformed(java.awt.event.ActionEvent evt) {
696  zoomOutBtnActionPerformed(evt);
697  }
698  });
699  gridBagConstraints = new java.awt.GridBagConstraints();
700  gridBagConstraints.gridx = 0;
701  gridBagConstraints.gridy = 2;
702  zoomPanel.add(zoomOutBtn, gridBagConstraints);
703 
704  gridBagConstraints = new java.awt.GridBagConstraints();
705  gridBagConstraints.anchor = java.awt.GridBagConstraints.SOUTHWEST;
706  gridBagConstraints.weightx = 1.0;
707  gridBagConstraints.weighty = 1.0;
708  gridBagConstraints.insets = new java.awt.Insets(4, 4, 4, 4);
709  mapViewer.add(zoomPanel, gridBagConstraints);
710 
711  add(mapViewer, java.awt.BorderLayout.CENTER);
712  }// </editor-fold>//GEN-END:initComponents
713 
714  private void zoomSliderStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_zoomSliderStateChanged
715  if (!zoomChanging) {
716  setZoom(zoomSlider.getValue());
717  }
718  }//GEN-LAST:event_zoomSliderStateChanged
719 
720  private void mapViewerMousePressed(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_mapViewerMousePressed
721  if (evt.isPopupTrigger()) {
722  showPopupMenu(evt.getPoint());
723  }
724  }//GEN-LAST:event_mapViewerMousePressed
725 
726  private void mapViewerMouseReleased(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_mapViewerMouseReleased
727  if (evt.isPopupTrigger()) {
728  showPopupMenu(evt.getPoint());
729  }
730  }//GEN-LAST:event_mapViewerMouseReleased
731 
732  private void mapViewerMouseMoved(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_mapViewerMouseMoved
733  GeoPosition geopos = mapViewer.convertPointToGeoPosition(evt.getPoint());
734  firePropertyChange(CURRENT_MOUSE_GEOPOSITION, null, geopos);
735  }//GEN-LAST:event_mapViewerMouseMoved
736 
737  private void mapViewerMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_mapViewerMouseClicked
738  if (!evt.isPopupTrigger() && SwingUtilities.isLeftMouseButton(evt)) {
739  List<MapWaypoint> waypoints = findClosestWaypoint(evt.getPoint());
740  if (waypoints.size() > 0) {
741  MapWaypoint selection = waypoints.get(0);
742  currentlySelectedWaypoint = selection;
743  currentlySelectedSet = null;
744  for (Set<MapWaypoint> track : tracks) {
745  if (track.contains(selection)) {
746  currentlySelectedSet = track;
747  break;
748  }
749  }
750  if (currentlySelectedSet == null) {
751  for (Set<MapWaypoint> area : areas) {
752  if (area.contains(selection)) {
753  currentlySelectedSet = area;
754  break;
755  }
756  }
757  }
758  } else {
759  currentlySelectedWaypoint = null;
760  currentlySelectedSet = null;
761  }
762  showDetailsPopup();
763  }
764  }//GEN-LAST:event_mapViewerMouseClicked
765 
766  private void zoomInBtnActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_zoomInBtnActionPerformed
767  int currentValue = mapViewer.getZoom();
768  setZoom(currentValue - 1);
769  }//GEN-LAST:event_zoomInBtnActionPerformed
770 
771  private void zoomOutBtnActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_zoomOutBtnActionPerformed
772  int currentValue = mapViewer.getZoom();
773  setZoom(currentValue + 1);
774  }//GEN-LAST:event_zoomOutBtnActionPerformed
775 
776 
777  // Variables declaration - do not modify//GEN-BEGIN:variables
778  private org.jxmapviewer.JXMapViewer mapViewer;
779  private javax.swing.JPanel zoomPanel;
780  private javax.swing.JSlider zoomSlider;
781  // End of variables declaration//GEN-END:variables
782 
786  private class MapWaypointRenderer implements WaypointRenderer<MapWaypoint> {
787 
788  private final Map<Color, BufferedImage> dotImageCache = new HashMap<>();
789  private final Map<Color, BufferedImage> verySmallDotImageCache = new HashMap<>();
790  private final Map<Color, BufferedImage> waypointImageCache = new HashMap<>();
791 
798  private Color getColor(MapWaypoint waypoint) {
799  Color baseColor = waypoint.getColor();
800  if (waypoint.equals(currentlySelectedWaypoint)
801  || (currentlySelectedSet != null && currentlySelectedSet.contains(waypoint))) {
802  // Highlight this waypoint since it is selected
803  return Color.YELLOW;
804  } else {
805  return baseColor;
806  }
807  }
808 
817  private BufferedImage createTrackDotImage(Color color, int s) {
818 
819  BufferedImage ret = new BufferedImage(s, s, BufferedImage.TYPE_INT_ARGB);
820  Graphics2D g = ret.createGraphics();
821  g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
822  g.setColor(color);
823  g.fillOval(1, 1, s - 2, s - 2);
824  g.setColor(Color.BLACK);
825  g.setStroke(new BasicStroke(1));
826  g.drawOval(1, 1, s - 2, s - 2);
827  g.dispose();
828  return ret;
829  }
830 
838  private BufferedImage createWaypointImage(Color color) {
839  int w = whiteWaypointImage.getWidth();
840  int h = whiteWaypointImage.getHeight();
841 
842  BufferedImage ret = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
843 
844  Graphics2D g = ret.createGraphics();
845  g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
846  g.drawImage(whiteWaypointImage, 0, 0, null);
847  g.setComposite(AlphaComposite.SrcIn);
848  g.setColor(color);
849  g.fillRect(0, 0, w, h);
850  g.setComposite(AlphaComposite.SrcAtop);
851  g.drawImage(transparentWaypointImage, 0, 0, null);
852  g.dispose();
853  return ret;
854  }
855 
856  @Override
857  public void paintWaypoint(Graphics2D g, JXMapViewer map, MapWaypoint waypoint) {
858  Color color = getColor(waypoint);
859  BufferedImage image;
860  Point2D point = map.getTileFactory().geoToPixel(waypoint.getPosition(), map.getZoom());
861  int x = (int) point.getX();
862  int y = (int) point.getY();
863 
864  if (DOT_WAYPOINT_TYPES.contains(waypoint.getArtifactTypeID())) {
865  image = dotImageCache.computeIfAbsent(color, k -> {
866  return createTrackDotImage(color, DOT_SIZE);
867  });
868  // Center the dot on the GPS coordinate
869  y -= image.getHeight() / 2;
870  } else if (VERY_SMALL_DOT_WAYPOINT_TYPES.contains(waypoint.getArtifactTypeID())) {
871  image = verySmallDotImageCache.computeIfAbsent(color, k -> {
872  return createTrackDotImage(color, VERY_SMALL_DOT_SIZE);
873  });
874  // Center the dot on the GPS coordinate
875  y -= image.getHeight() / 2;
876  } else {
877  image = waypointImageCache.computeIfAbsent(color, k -> {
878  return createWaypointImage(color);
879  });
880  // Align the bottom of the pin with the GPS coordinate
881  y -= image.getHeight();
882  }
883  // Center image horizontally on image
884  x -= image.getWidth() / 2;
885 
886  Graphics2D g2d = (Graphics2D) g.create();
887  g2d.drawImage(image, x, y, null);
888  g2d.dispose();
889  }
890  }
891 
895  private class MapTrackRenderer implements Painter<JXMapViewer> {
896 
897  private final List<Set<MapWaypoint>> tracks;
898 
899  MapTrackRenderer(List<Set<MapWaypoint>> tracks) {
900  this.tracks = tracks;
901  }
902 
903  private void drawRoute(Set<MapWaypoint> track, Graphics2D g, JXMapViewer map) {
904  int lastX = 0;
905  int lastY = 0;
906 
907  boolean first = true;
908 
909  for (MapWaypoint wp : track) {
910  Point2D p = map.getTileFactory().geoToPixel(wp.getPosition(), map.getZoom());
911  int thisX = (int) p.getX();
912  int thisY = (int) p.getY();
913 
914  if (first) {
915  first = false;
916  } else {
917  g.drawLine(lastX, lastY, thisX, thisY);
918  }
919 
920  lastX = thisX;
921  lastY = thisY;
922  }
923  }
924 
925  @Override
926  public void paint(Graphics2D g, JXMapViewer map, int w, int h) {
927  Graphics2D g2d = (Graphics2D) g.create();
928 
929  Rectangle bounds = map.getViewportBounds();
930  g2d.translate(-bounds.x, -bounds.y);
931 
932  g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
933 
934  g2d.setColor(Color.BLACK);
935  g2d.setStroke(new BasicStroke(2));
936 
937  for (Set<MapWaypoint> track : tracks) {
938  drawRoute(track, g2d, map);
939  }
940 
941  g2d.dispose();
942  }
943  }
944 
948  private class MapAreaRenderer implements Painter<JXMapViewer> {
949 
950  private final List<Set<MapWaypoint>> areas;
951 
952  MapAreaRenderer(List<Set<MapWaypoint>> areas) {
953  this.areas = areas;
954  }
955 
963  private void drawArea(Set<MapWaypoint> area, Graphics2D g, JXMapViewer map) {
964  if (area.isEmpty()) {
965  return;
966  }
967  boolean first = true;
968 
969  GeneralPath polygon = new GeneralPath(GeneralPath.WIND_EVEN_ODD, area.size());
970 
971  for (MapWaypoint wp : area) {
972  Point2D p = map.getTileFactory().geoToPixel(wp.getPosition(), map.getZoom());
973  int thisX = (int) p.getX();
974  int thisY = (int) p.getY();
975 
976  if (first) {
977  polygon.moveTo(thisX, thisY);
978  first = false;
979  } else {
980  polygon.lineTo(thisX, thisY);
981  }
982  }
983  polygon.closePath();
984 
985  Color areaColor = area.iterator().next().getColor();
986  final double maxColorValue = 255.0;
987  g.setPaint(new Color((float)(areaColor.getRed() / maxColorValue),
988  (float)(areaColor.getGreen() / maxColorValue),
989  (float)(areaColor.getBlue() / maxColorValue),
990  .2f));
991  g.fill(polygon);
992  g.draw(polygon);
993  }
994 
995  @Override
996  public void paint(Graphics2D g, JXMapViewer map, int w, int h) {
997  Graphics2D g2d = (Graphics2D) g.create();
998 
999  Rectangle bounds = map.getViewportBounds();
1000  g2d.translate(-bounds.x, -bounds.y);
1001 
1002  g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
1003 
1004  g2d.setColor(Color.BLACK);
1005  g2d.setStroke(new BasicStroke(2));
1006 
1007  for (Set<MapWaypoint> area : areas) {
1008  drawArea(area, g2d, map);
1009  }
1010 
1011  g2d.dispose();
1012  }
1013  }
1014 }
void paint(Graphics2D g, JXMapViewer map, int w, int h)
Definition: MapPanel.java:996
void zoomInBtnActionPerformed(java.awt.event.ActionEvent evt)
Definition: MapPanel.java:766
void showPopupMenu(MapWaypoint waypoint, Point point)
Definition: MapPanel.java:421
void zoomSliderStateChanged(javax.swing.event.ChangeEvent evt)
Definition: MapPanel.java:714
void mapViewerMouseReleased(java.awt.event.MouseEvent evt)
Definition: MapPanel.java:726
Collection< T > nearestNeighbourSearch(int numNeighbors, T value)
Definition: KdTree.java:256
TileFactoryInfo createOnlineOSMFactory(String address)
Definition: MapPanel.java:297
void mapViewerMouseClicked(java.awt.event.MouseEvent evt)
Definition: MapPanel.java:737
void mapViewerMousePressed(java.awt.event.MouseEvent evt)
Definition: MapPanel.java:720
void mapViewerMouseMoved(java.awt.event.MouseEvent evt)
Definition: MapPanel.java:732
void drawRoute(Set< MapWaypoint > track, Graphics2D g, JXMapViewer map)
Definition: MapPanel.java:903
List< MapWaypoint > findClosestWaypoint(Point clickPoint)
Definition: MapPanel.java:500
void drawArea(Set< MapWaypoint > area, Graphics2D g, JXMapViewer map)
Definition: MapPanel.java:963
TileFactoryInfo createOSMZipFactory(String path)
Definition: MapPanel.java:315
void paintWaypoint(Graphics2D g, JXMapViewer map, MapWaypoint waypoint)
Definition: MapPanel.java:857
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:771
static void addChangeListener(PreferenceChangeListener listener)
void paint(Graphics2D g, JXMapViewer map, int w, int h)
Definition: MapPanel.java:926
org.jxmapviewer.JXMapViewer mapViewer
Definition: MapPanel.java:778

Copyright © 2012-2021 Basis Technology. Generated on: Fri Aug 6 2021
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.