19 package org.sleuthkit.autopsy.geolocation;
 
   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;
 
   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;
 
   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;
 
   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;
 
   86 @SuppressWarnings(
"deprecation")
 
   87 final public class 
MapPanel extends javax.swing.JPanel {
 
   89     static final String CURRENT_MOUSE_GEOPOSITION = 
"CURRENT_MOUSE_GEOPOSITION";
 
   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;
 
  102     private List<Set<MapWaypoint>> tracks = 
new ArrayList<>();
 
  103     private List<Set<MapWaypoint>> areas = 
new ArrayList<>();
 
  108     private static final int POPUP_WIDTH = 300;
 
  109     private static final int POPUP_HEIGHT = 200;
 
  110     private static final int POPUP_MARGIN = 10;
 
  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());
 
  129         "MapPanel_connection_failure_message=Failed to connect to new geolocation map tile source.",
 
  130         "MapPanel_connection_failure_message_title=Connection Failure" 
  135         zoomChanging = 
false;
 
  137         popupFactory = 
new PopupFactory();
 
  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);
 
  152             public void resizeTimedOut() {
 
  159             public void preferenceChange(PreferenceChangeEvent evt) {
 
  162                     mapViewer.getTileFactory().dispose();
 
  164                     mapViewer.setTileFactory(getTileFactory());
 
  165                     initializeZoomSlider();
 
  167                     logger.log(Level.SEVERE, 
"Failed to connect to new geolocation tile server.", ex); 
 
  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());
 
  185     List<MapWaypoint> getVisibleWaypoints() {
 
  187         Rectangle viewport = mapViewer.getViewportBounds();
 
  188         List<MapWaypoint> waypoints = 
new ArrayList<>();
 
  190         Iterator<MapWaypoint> iterator = waypointTree.
iterator();
 
  191         while (iterator.hasNext()) {
 
  193             if (viewport.contains(mapViewer.getTileFactory().geoToPixel(waypoint.
getPosition(), mapViewer.getZoom()))) {
 
  194                 waypoints.add(waypoint);
 
  204     void initMap() throws GeoLocationDataException {
 
  206         TileFactory tileFactory = getTileFactory();
 
  207         mapViewer.setTileFactory(tileFactory);
 
  210         MouseInputListener mia = 
new MapPanMouseInputListener(mapViewer);
 
  211         mapViewer.addMouseListener(mia);
 
  212         mapViewer.addMouseMotionListener(mia);
 
  214         mapViewer.addMouseListener(
new CenterMapListener(mapViewer));
 
  215         mapViewer.addMouseWheelListener(
new ZoomMouseWheelListenerCursor(mapViewer));
 
  218         mapViewer.addPropertyChangeListener(
"zoom", 
new PropertyChangeListener() {
 
  220             public void propertyChange(PropertyChangeEvent evt) {
 
  221                 zoomSlider.setValue(mapViewer.getZoom());
 
  225         zoomSlider.setMinimum(tileFactory.getInfo().getMinimumZoomLevel());
 
  226         zoomSlider.setMaximum(tileFactory.getInfo().getMaximumZoomLevel());
 
  228         setZoom(tileFactory.getInfo().getMaximumZoomLevel() - 1);
 
  230         mapViewer.setCenterPosition(
new GeoPosition(0, 0));
 
  235     void initializePainter() {
 
  237         WaypointPainter<MapWaypoint> waypointPainter = 
new WaypointPainter<MapWaypoint>() {
 
  239             public Set<MapWaypoint> getWaypoints() {
 
  240                 if (currentlySelectedWaypoint != null) {
 
  241                     waypointSet.remove(currentlySelectedWaypoint);
 
  242                     waypointSet.add(currentlySelectedWaypoint);
 
  247         waypointPainter.setRenderer(
new MapWaypointRenderer());
 
  249         ArrayList<Painter<JXMapViewer>> painters = 
new ArrayList<>();
 
  250         painters.add(
new MapAreaRenderer(areas));
 
  251         painters.add(
new MapTrackRenderer(tracks));
 
  252         painters.add(waypointPainter);
 
  254         CompoundPainter<JXMapViewer> compoundPainter = 
new CompoundPainter<>(painters);
 
  255         mapViewer.setOverlayPainter(compoundPainter);
 
  261     void initializeZoomSlider() {
 
  262         TileFactory tileFactory = mapViewer.getTileFactory();
 
  263         zoomSlider.setMinimum(tileFactory.getInfo().getMinimumZoomLevel());
 
  264         zoomSlider.setMaximum(tileFactory.getInfo().getMaximumZoomLevel());
 
  266         zoomSlider.repaint();
 
  267         zoomSlider.revalidate();
 
  277             case ONLINE_USER_DEFINED_OSM_SERVER:
 
  279             case OFFLINE_OSM_ZIP:
 
  281             case OFFILE_MBTILES_FILE:
 
  284                 return new DefaultTileFactory(
new VirtualEarthTileFactoryInfo(VirtualEarthTileFactoryInfo.MAP));
 
  298         if (address.isEmpty()) {
 
  299             throw new GeoLocationDataException(
"Invalid user preference for OSM user define tile server. Address is an empty string.");
 
  301             TileFactoryInfo info = 
new OSMTileFactoryInfo(
"User Defined Server", address);
 
  316         if (path.isEmpty()) {
 
  317             throw new GeoLocationDataException(
"Invalid OSM tile Zip file. User preference value is empty string.");
 
  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);
 
  324             String zipPath = path.replaceAll(
"\\\\", 
"/");
 
  326             return new OSMTileFactoryInfo(
"ZIP archive", 
"jar:file:/" + zipPath + 
"!");  
 
  335     void setWaypoints(Set<MapWaypoint> waypoints) {
 
  337         this.waypointSet = waypoints;
 
  339             waypointTree.
add(waypoint);
 
  349     void setTracks(List<Set<MapWaypoint>> tracks) {
 
  350         this.tracks = tracks;
 
  358     void setAreas(List<Set<MapWaypoint>> areas) {
 
  367     void setZoom(
int zoom) {
 
  369         mapViewer.setZoom(zoom);
 
  370         zoomSlider.setValue(zoom);
 
  371         zoomChanging = 
false;
 
  377     void clearWaypoints() {
 
  379         currentlySelectedWaypoint = null;
 
  380         currentlySelectedSet = null;
 
  381         if (currentPopup != null) {
 
  395             List<MapWaypoint> waypoints = findClosestWaypoint(point);
 
  397             if (waypoints.size() > 0) {
 
  398                 waypoint = waypoints.get(0);
 
  400             showPopupMenu(waypoint, point);
 
  403             if (waypoint != null && !waypoint.
equals(currentlySelectedWaypoint)) {
 
  404                 currentlySelectedWaypoint = waypoint;
 
  405                 if (currentPopup != null) {
 
  410         } 
catch (TskCoreException ex) {
 
  411             logger.log(Level.WARNING, 
"Failed to show popup for waypoint", ex);
 
  422         if (waypoint == null) {
 
  426         JMenuItem[] items = waypoint.getMenuItems();
 
  427         JPopupMenu popupMenu = 
new JPopupMenu();
 
  428         for (JMenuItem menu : items) {
 
  433                 popupMenu.add(
new JSeparator());
 
  436         popupMenu.show(mapViewer, point.x, point.y);
 
  443         if (currentlySelectedWaypoint != null) {
 
  444             if (currentPopup != null) {
 
  448             WaypointDetailPanel detailPane = 
new WaypointDetailPanel();
 
  449             detailPane.setWaypoint(currentlySelectedWaypoint);
 
  450             detailPane.setPreferredSize(
new Dimension(POPUP_WIDTH, POPUP_HEIGHT));
 
  451             detailPane.addActionListener(
new ActionListener() {
 
  453                 public void actionPerformed(ActionEvent e) {
 
  454                     if (currentPopup != null) {
 
  462             Point popupLocation = getLocationForDetailsPopup();
 
  464             currentPopup = popupFactory.getPopup(
this, detailPane, popupLocation.x, popupLocation.y);
 
  468             if (currentPopup != null) {
 
  473         mapViewer.revalidate();
 
  483         Point panelCorner = this.getLocationOnScreen();
 
  484         int width = this.getWidth();
 
  486         int popupX = panelCorner.x + width - POPUP_WIDTH - POPUP_MARGIN;
 
  487         int popupY = panelCorner.y + POPUP_MARGIN;
 
  489         return new Point(popupX, popupY);
 
  501         if (waypointTree == null) {
 
  502             return new ArrayList<>();
 
  506         GeoPosition geopos = mapViewer.convertPointToGeoPosition(clickPoint);
 
  511         if (waypoints == null || waypoints.isEmpty()) {
 
  515         Iterator<MapWaypoint> iterator = waypoints.iterator();
 
  519         List<MapWaypoint> closestPoints = 
new ArrayList<>();
 
  520         while (iterator.hasNext()) {
 
  522             Point2D point = mapViewer.convertGeoPositionToPoint(nextWaypoint.
getPosition());
 
  523             int pointX = (int) point.
getX();
 
  524             int pointY = (int) point.getY();
 
  526             if (DOT_WAYPOINT_TYPES.contains(nextWaypoint.getArtifactTypeID())) {
 
  527                 rect = 
new Rectangle(
 
  528                         pointX - (DOT_SIZE / 2),
 
  529                         pointY - (DOT_SIZE / 2),
 
  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),
 
  541                 rect = 
new Rectangle(
 
  542                         pointX - (whiteWaypointImage.getWidth() / 2),
 
  543                         pointY - whiteWaypointImage.getHeight(),
 
  544                         whiteWaypointImage.getWidth(),
 
  545                         whiteWaypointImage.getHeight()
 
  549             if (rect.contains(clickPoint)) {
 
  550                 closestPoints.add(nextWaypoint);
 
  554         return closestPoints;
 
  567             extends ComponentAdapter
 
  568             implements ActionListener {
 
  571         private static final int DEFAULT_TIMEOUT = 200;
 
  578             this(DEFAULT_TIMEOUT);
 
  587             timer = 
new Timer(delayMS, 
this);
 
  588             timer.setRepeats(
false);
 
  589             timer.setCoalesce(
false);
 
  605         public abstract void resizeTimedOut();
 
  613     @SuppressWarnings(
"unchecked")
 
  615     private 
void initComponents() {
 
  616         java.awt.GridBagConstraints gridBagConstraints;
 
  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();
 
  625         setLayout(
new java.awt.BorderLayout());
 
  627         mapViewer.addMouseMotionListener(
new java.awt.event.MouseMotionAdapter() {
 
  628             public void mouseMoved(java.awt.event.MouseEvent evt) {
 
  629                 mapViewerMouseMoved(evt);
 
  632         mapViewer.addMouseListener(
new java.awt.event.MouseAdapter() {
 
  633             public void mouseClicked(java.awt.event.MouseEvent evt) {
 
  634                 mapViewerMouseClicked(evt);
 
  636             public void mousePressed(java.awt.event.MouseEvent evt) {
 
  637                 mapViewerMousePressed(evt);
 
  639             public void mouseReleased(java.awt.event.MouseEvent evt) {
 
  640                 mapViewerMouseReleased(evt);
 
  643         mapViewer.setLayout(
new java.awt.GridBagLayout());
 
  645         zoomPanel.setFocusable(
false);
 
  646         zoomPanel.setOpaque(
false);
 
  647         zoomPanel.setRequestFocusEnabled(
false);
 
  648         zoomPanel.setLayout(
new java.awt.GridBagLayout());
 
  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);
 
  665         gridBagConstraints = 
new java.awt.GridBagConstraints();
 
  666         gridBagConstraints.gridx = 0;
 
  667         gridBagConstraints.gridy = 1;
 
  668         zoomPanel.add(zoomSlider, gridBagConstraints);
 
  670         zoomInBtn.setIcon(
new javax.swing.ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/images/plus-grey.png"))); 
 
  671         org.openide.awt.Mnemonics.setLocalizedText(zoomInBtn, 
org.openide.util.NbBundle.getMessage(
MapPanel.class, 
"MapPanel.zoomInBtn.text")); 
 
  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);
 
  682         gridBagConstraints = 
new java.awt.GridBagConstraints();
 
  683         gridBagConstraints.gridx = 0;
 
  684         gridBagConstraints.gridy = 0;
 
  685         zoomPanel.add(zoomInBtn, gridBagConstraints);
 
  687         zoomOutBtn.setIcon(
new javax.swing.ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/images/minus-grey.png"))); 
 
  688         org.openide.awt.Mnemonics.setLocalizedText(zoomOutBtn, 
org.openide.util.NbBundle.getMessage(
MapPanel.class, 
"MapPanel.zoomOutBtn.text")); 
 
  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);
 
  699         gridBagConstraints = 
new java.awt.GridBagConstraints();
 
  700         gridBagConstraints.gridx = 0;
 
  701         gridBagConstraints.gridy = 2;
 
  702         zoomPanel.add(zoomOutBtn, gridBagConstraints);
 
  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);
 
  711         add(mapViewer, java.awt.BorderLayout.CENTER);
 
  716             setZoom(zoomSlider.getValue());
 
  721         if (evt.isPopupTrigger()) {
 
  722             showPopupMenu(evt.getPoint());
 
  727         if (evt.isPopupTrigger()) {
 
  728             showPopupMenu(evt.getPoint());
 
  733         GeoPosition geopos = mapViewer.convertPointToGeoPosition(evt.getPoint());
 
  734         firePropertyChange(CURRENT_MOUSE_GEOPOSITION, null, geopos);
 
  738         if (!evt.isPopupTrigger() && SwingUtilities.isLeftMouseButton(evt)) {
 
  739             List<MapWaypoint> waypoints = findClosestWaypoint(evt.getPoint());
 
  740             if (waypoints.size() > 0) {
 
  742                 currentlySelectedWaypoint = selection;
 
  743                 currentlySelectedSet = null;
 
  744                 for (Set<MapWaypoint> track : tracks) {
 
  745                     if (track.contains(selection)) {
 
  746                         currentlySelectedSet = track;
 
  750                 if (currentlySelectedSet == null) {
 
  751                     for (Set<MapWaypoint> area : areas) {
 
  752                         if (area.contains(selection)) {
 
  753                             currentlySelectedSet = area;
 
  759                 currentlySelectedWaypoint = null;
 
  760                 currentlySelectedSet = null;
 
  767         int currentValue = mapViewer.getZoom();
 
  768         setZoom(currentValue - 1);
 
  772         int currentValue = mapViewer.getZoom();
 
  773         setZoom(currentValue + 1);
 
  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<>();
 
  799             Color baseColor = waypoint.getColor();
 
  800             if (waypoint.
equals(currentlySelectedWaypoint)
 
  801                     || (currentlySelectedSet != null && currentlySelectedSet.contains(waypoint))) {
 
  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);
 
  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);
 
  839             int w = whiteWaypointImage.getWidth();
 
  840             int h = whiteWaypointImage.getHeight();
 
  842             BufferedImage ret = 
new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
 
  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);
 
  849             g.fillRect(0, 0, w, h);
 
  850             g.setComposite(AlphaComposite.SrcAtop);
 
  851             g.drawImage(transparentWaypointImage, 0, 0, null);
 
  858             Color color = getColor(waypoint);
 
  860             Point2D point = map.getTileFactory().geoToPixel(waypoint.
getPosition(), map.getZoom());
 
  861             int x = (int) point.getX();
 
  862             int y = (int) point.getY();
 
  864             if (DOT_WAYPOINT_TYPES.contains(waypoint.getArtifactTypeID())) {
 
  865                 image = dotImageCache.computeIfAbsent(color, k -> {
 
  866                     return createTrackDotImage(color, DOT_SIZE);
 
  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);
 
  875                 y -= image.getHeight() / 2;
 
  877                 image = waypointImageCache.computeIfAbsent(color, k -> {
 
  878                     return createWaypointImage(color);
 
  881                 y -= image.getHeight();
 
  884             x -= image.getWidth() / 2;
 
  886             Graphics2D g2d = (Graphics2D) g.create();
 
  887             g2d.drawImage(image, x, y, null);
 
  897         private final List<Set<MapWaypoint>> 
tracks;
 
  900             this.tracks = tracks;
 
  903         private void drawRoute(Set<MapWaypoint> track, Graphics2D g, JXMapViewer map) {
 
  907             boolean first = 
true;
 
  910                 Point2D p = map.getTileFactory().geoToPixel(wp.getPosition(), map.getZoom());
 
  911                 int thisX = (int) p.getX();
 
  912                 int thisY = (int) p.getY();
 
  917                     g.drawLine(lastX, lastY, thisX, thisY);
 
  926         public void paint(Graphics2D g, JXMapViewer map, 
int w, 
int h) {
 
  927             Graphics2D g2d = (Graphics2D) g.create();
 
  929             Rectangle bounds = map.getViewportBounds();
 
  930             g2d.translate(-bounds.x, -bounds.y);
 
  932             g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
 
  934             g2d.setColor(Color.BLACK);
 
  935             g2d.setStroke(
new BasicStroke(2));
 
  937             for (Set<MapWaypoint> track : tracks) {
 
  938                 drawRoute(track, g2d, map);
 
  950         private final List<Set<MapWaypoint>> 
areas;
 
  963         private void drawArea(Set<MapWaypoint> area, Graphics2D g, JXMapViewer map) {
 
  964             if (area.isEmpty()) {
 
  967             boolean first = 
true;
 
  969             GeneralPath polygon = 
new GeneralPath(GeneralPath.WIND_EVEN_ODD, area.size());
 
  972                 Point2D p = map.getTileFactory().geoToPixel(wp.getPosition(), map.getZoom());
 
  973                 int thisX = (int) p.getX();
 
  974                 int thisY = (int) p.getY();
 
  977                     polygon.moveTo(thisX, thisY);
 
  980                     polygon.lineTo(thisX, thisY);
 
  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),
 
  996         public void paint(Graphics2D g, JXMapViewer map, 
int w, 
int h) {
 
  997             Graphics2D g2d = (Graphics2D) g.create();
 
  999             Rectangle bounds = map.getViewportBounds();
 
 1000             g2d.translate(-bounds.x, -bounds.y);
 
 1002             g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
 
 1004             g2d.setColor(Color.BLACK);
 
 1005             g2d.setStroke(
new BasicStroke(2));
 
 1007             for (Set<MapWaypoint> area : areas) {
 
 1008                 drawArea(area, g2d, map);
 
void paint(Graphics2D g, JXMapViewer map, int w, int h)
final List< Set< MapWaypoint > > tracks
void zoomInBtnActionPerformed(java.awt.event.ActionEvent evt)
static String getGeolocationOsmZipPath()
final PopupFactory popupFactory
void showPopupMenu(MapWaypoint waypoint, Point point)
boolean equals(Object obj)
BufferedImage createWaypointImage(Color color)
void zoomSliderStateChanged(javax.swing.event.ChangeEvent evt)
void componentResized(ComponentEvent e)
void mapViewerMouseReleased(java.awt.event.MouseEvent evt)
Collection< T > nearestNeighbourSearch(int numNeighbors, T value)
ComponentResizeEndListener()
javax.swing.JPanel zoomPanel
KdTree< MapWaypoint > waypointTree
static String getGeolocationMBTilesFilePath()
TileFactoryInfo createOnlineOSMFactory(String address)
BufferedImage whiteWaypointImage
javax.swing.JSlider zoomSlider
void mapViewerMouseClicked(java.awt.event.MouseEvent evt)
void mapViewerMousePressed(java.awt.event.MouseEvent evt)
void mapViewerMouseMoved(java.awt.event.MouseEvent evt)
void drawRoute(Set< MapWaypoint > track, Graphics2D g, JXMapViewer map)
Set< MapWaypoint > currentlySelectedSet
Color getColor(MapWaypoint waypoint)
List< MapWaypoint > findClosestWaypoint(Point clickPoint)
void drawArea(Set< MapWaypoint > area, Graphics2D g, JXMapViewer map)
static int getGeolocationtTileOption()
Point getLocationForDetailsPopup()
void actionPerformed(ActionEvent e)
final List< Set< MapWaypoint > > areas
TileFactoryInfo createOSMZipFactory(String path)
BufferedImage createTrackDotImage(Color color, int s)
static String getGeolocationOsmServerAddress()
void paintWaypoint(Graphics2D g, JXMapViewer map, MapWaypoint waypoint)
static void error(String title, String message)
TileFactory getTileFactory()
synchronized static Logger getLogger(String name)
void zoomOutBtnActionPerformed(java.awt.event.ActionEvent evt)
static void addChangeListener(PreferenceChangeListener listener)
ComponentResizeEndListener(int delayMS)
void paint(Graphics2D g, JXMapViewer map, int w, int h)
BufferedImage transparentWaypointImage
GeoPosition getPosition()
Set< MapWaypoint > waypointSet
org.jxmapviewer.JXMapViewer mapViewer
void showPopupMenu(Point point)
MapWaypoint currentlySelectedWaypoint