Autopsy  4.13.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.Dimension;
22 import java.awt.Graphics2D;
23 import java.awt.Point;
24 import java.awt.Rectangle;
25 import java.awt.event.ActionEvent;
26 import java.awt.event.ActionListener;
27 import java.awt.event.ComponentAdapter;
28 import java.awt.event.ComponentEvent;
29 import java.awt.geom.Point2D;
30 import java.awt.image.BufferedImage;
31 import java.beans.PropertyChangeEvent;
32 import java.beans.PropertyChangeListener;
33 import java.io.File;
34 import java.io.IOException;
35 import java.util.ArrayList;
36 import java.util.Collection;
37 import java.util.Iterator;
38 import java.util.LinkedHashSet;
39 import java.util.List;
40 import java.util.Set;
41 import java.util.logging.Level;
42 import java.util.prefs.PreferenceChangeEvent;
43 import java.util.prefs.PreferenceChangeListener;
44 import javax.swing.JMenuItem;
45 import javax.swing.JOptionPane;
46 import javax.swing.JPopupMenu;
47 import javax.swing.JSeparator;
48 import javax.swing.Popup;
49 import javax.swing.PopupFactory;
50 import javax.swing.Timer;
51 import javax.swing.event.MouseInputListener;
52 import org.jxmapviewer.JXMapViewer;
53 import org.jxmapviewer.OSMTileFactoryInfo;
54 import org.jxmapviewer.VirtualEarthTileFactoryInfo;
55 import org.jxmapviewer.input.CenterMapListener;
56 import org.jxmapviewer.input.ZoomMouseWheelListenerCursor;
57 import org.jxmapviewer.viewer.DefaultTileFactory;
58 import org.jxmapviewer.viewer.GeoPosition;
59 import org.jxmapviewer.viewer.TileFactory;
60 import org.jxmapviewer.viewer.TileFactoryInfo;
61 import org.jxmapviewer.viewer.Waypoint;
62 import org.jxmapviewer.viewer.WaypointPainter;
63 import org.jxmapviewer.viewer.WaypointRenderer;
64 import org.openide.util.NbBundle.Messages;
69 import org.sleuthkit.datamodel.TskCoreException;
70 import javax.imageio.ImageIO;
71 import javax.swing.SwingUtilities;
72 import org.jxmapviewer.viewer.DefaultWaypointRenderer;
73 
77 final public class MapPanel extends javax.swing.JPanel {
78 
79  static final String CURRENT_MOUSE_GEOPOSITION = "CURRENT_MOUSE_GEOPOSITION";
80  private static final Logger logger = Logger.getLogger(MapPanel.class.getName());
81 
82  private static final long serialVersionUID = 1L;
83  private boolean zoomChanging;
85 
86  private Popup currentPopup;
87  private final PopupFactory popupFactory;
88 
89  private static final int POPUP_WIDTH = 300;
90  private static final int POPUP_HEIGHT = 200;
91  private static final int POPUP_MARGIN = 10;
92 
93  private MapWaypoint currentlySelectedWaypoint;
94 
98  @Messages({
99  "MapPanel_connection_failure_message=Failed to connect to new geolocation map tile source.",
100  "MapPanel_connection_failure_message_title=Connection Failure"
101  })
102  public MapPanel() {
103  initComponents();
104 
105  zoomChanging = false;
106  currentPopup = null;
107  popupFactory = new PopupFactory();
108 
109  // ComponentListeners do not have a concept of resize event "complete"
110  // therefore if we move the popup as the window resizes there will be
111  // a weird blinking behavior. Using the CompnentResizeEndListener the
112  // popup will move to its corner one the resize is completed.
113  this.addComponentListener(new ComponentResizeEndListener() {
114  @Override
115  public void resizeTimedOut() {
117  }
118  });
119 
120  UserPreferences.addChangeListener(new PreferenceChangeListener() {
121  @Override
122  public void preferenceChange(PreferenceChangeEvent evt) {
123  try {
124  // Tell the old factory to cleanup
125  mapViewer.getTileFactory().dispose();
126 
127  mapViewer.setTileFactory(getTileFactory());
128  initializeZoomSlider();
129  } catch (GeoLocationDataException ex) {
130  logger.log(Level.SEVERE, "Failed to connect to new geolocation tile server.", ex); //NON-NLS
131  JOptionPane.showMessageDialog(MapPanel.this,
132  Bundle.MapPanel_connection_failure_message(),
133  Bundle.MapPanel_connection_failure_message_title(),
134  JOptionPane.ERROR_MESSAGE);
136  Bundle.MapPanel_connection_failure_message_title(),
137  Bundle.MapPanel_connection_failure_message());
138  }
139  }
140  });
141 
142 
143  }
144 
150  List<MapWaypoint> getVisibleWaypoints() {
151 
152  Rectangle viewport = mapViewer.getViewportBounds();
153  List<MapWaypoint> waypoints = new ArrayList<>();
154 
155  Iterator<MapWaypoint> iterator = waypointTree.iterator();
156  while (iterator.hasNext()) {
157  MapWaypoint waypoint = iterator.next();
158  if (viewport.contains(mapViewer.getTileFactory().geoToPixel(waypoint.getPosition(), mapViewer.getZoom()))) {
159  waypoints.add(waypoint);
160  }
161  }
162 
163  return waypoints;
164  }
165 
169  void initMap() throws GeoLocationDataException {
170 
171  TileFactory tileFactory = getTileFactory();
172  mapViewer.setTileFactory(tileFactory);
173 
174  // Add Mouse interactions
175  MouseInputListener mia = new MapPanMouseInputListener(mapViewer);
176  mapViewer.addMouseListener(mia);
177  mapViewer.addMouseMotionListener(mia);
178 
179  mapViewer.addMouseListener(new CenterMapListener(mapViewer));
180  mapViewer.addMouseWheelListener(new ZoomMouseWheelListenerCursor(mapViewer));
181 
182  // Listen to the map for a change in zoom so that we can update the slider.
183  mapViewer.addPropertyChangeListener("zoom", new PropertyChangeListener() {
184  @Override
185  public void propertyChange(PropertyChangeEvent evt) {
186  zoomSlider.setValue(mapViewer.getZoom());
187  }
188  });
189 
190  zoomSlider.setMinimum(tileFactory.getInfo().getMinimumZoomLevel());
191  zoomSlider.setMaximum(tileFactory.getInfo().getMaximumZoomLevel());
192 
193  setZoom(tileFactory.getInfo().getMaximumZoomLevel() - 1);
194 
195  mapViewer.setCenterPosition(new GeoPosition(0, 0));
196 
197  // Basic painters for the way points.
198  WaypointPainter<Waypoint> waypointPainter = new WaypointPainter<Waypoint>() {
199  @Override
200  public Set<Waypoint> getWaypoints() {
201  //To assure that the currentlySelectedWaypoint is visible it needs
202  // to be painted last. LinkedHashSet has a predicable ordering.
203  Set<Waypoint> set = new LinkedHashSet<>();
204  if (waypointTree != null) {
205  Iterator<MapWaypoint> iterator = waypointTree.iterator();
206  while (iterator.hasNext()) {
207  MapWaypoint point = iterator.next();
208  set.add(point);
209  }
210  // Add the currentlySelectedWaypoint to the end so that
211  // it will be painted last.
212  if (currentlySelectedWaypoint != null) {
213  set.remove(currentlySelectedWaypoint);
214  set.add(currentlySelectedWaypoint);
215  }
216  }
217  return set;
218  }
219  };
220 
221  try {
222  waypointPainter.setRenderer(new MapWaypointRenderer());
223  } catch (IOException ex) {
224  logger.log(Level.WARNING, "Failed to load waypoint image resource, using DefaultWaypointRenderer", ex);
225  waypointPainter.setRenderer(new DefaultWaypointRenderer());
226  }
227 
228  mapViewer.setOverlayPainter(waypointPainter);
229  }
230 
234  void initializeZoomSlider() {
235  TileFactory tileFactory = mapViewer.getTileFactory();
236  zoomSlider.setMinimum(tileFactory.getInfo().getMinimumZoomLevel());
237  zoomSlider.setMaximum(tileFactory.getInfo().getMaximumZoomLevel());
238 
239  zoomSlider.repaint();
240  zoomSlider.revalidate();
241  }
242 
248  private TileFactory getTileFactory() throws GeoLocationDataException {
249  switch (GeolocationSettingsPanel.GeolocationDataSourceType.getOptionForValue(UserPreferences.getGeolocationtTileOption())) {
250  case ONLINE_USER_DEFINED_OSM_SERVER:
252  case OFFLINE_OSM_ZIP:
253  return new DefaultTileFactory(createOSMZipFactory(UserPreferences.getGeolocationOsmZipPath()));
254  case OFFILE_MBTILES_FILE:
255  return new MBTilesTileFactory(UserPreferences.getGeolocationMBTilesFilePath());
256  default:
257  return new DefaultTileFactory(new VirtualEarthTileFactoryInfo(VirtualEarthTileFactoryInfo.MAP));
258  }
259  }
260 
270  private TileFactoryInfo createOnlineOSMFactory(String address) throws GeoLocationDataException {
271  if (address.isEmpty()) {
272  throw new GeoLocationDataException("Invalid user preference for OSM user define tile server. Address is an empty string.");
273  } else {
274  TileFactoryInfo info = new OSMTileFactoryInfo("User Defined Server", address);
275  return info;
276  }
277  }
278 
288  private TileFactoryInfo createOSMZipFactory(String path) throws GeoLocationDataException {
289  if (path.isEmpty()) {
290  throw new GeoLocationDataException("Invalid OSM tile Zip file. User preference value is empty string.");
291  } else {
292  File file = new File(path);
293  if (!file.exists() || !file.canRead()) {
294  throw new GeoLocationDataException("Invalid OSM tile zip file. Unable to read file: " + path);
295  }
296 
297  String zipPath = path.replaceAll("\\\\", "/");
298 
299  return new OSMTileFactoryInfo("ZIP archive", "jar:file:/" + zipPath + "!"); //NON-NLS
300  }
301  }
302 
308  void setWaypoints(List<MapWaypoint> waypoints) {
309  waypointTree = new KdTree<>();
310 
311  for (MapWaypoint waypoint : waypoints) {
312  waypointTree.add(waypoint);
313  }
314 
315  mapViewer.repaint();
316  }
317 
323  void setZoom(int zoom) {
324  zoomChanging = true;
325  mapViewer.setZoom(zoom);
326  zoomSlider.setValue((zoomSlider.getMaximum() + zoomSlider.getMinimum()) - zoom);
327  zoomChanging = false;
328  }
329 
333  void clearWaypoints() {
334  waypointTree = null;
335  currentlySelectedWaypoint = null;
336  if (currentPopup != null) {
337  currentPopup.hide();
338  }
339  mapViewer.repaint();
340  }
341 
348  private void showPopupMenu(Point point) {
349  try {
350  List<MapWaypoint> waypoints = findClosestWaypoint(point);
351  MapWaypoint waypoint = null;
352  if(waypoints.size() > 0) {
353  waypoint = waypoints.get(0);
354  }
355  showPopupMenu(waypoint, point);
356  // Change the details popup to the currently selected point only if
357  // it the popup is currently visible
358  if (waypoint != null && !waypoint.equals(currentlySelectedWaypoint)) {
359  currentlySelectedWaypoint = waypoint;
360  if(currentPopup != null) {
362  }
363  mapViewer.repaint();
364  }
365  } catch (TskCoreException ex) {
366  logger.log(Level.WARNING, "Failed to show popup for waypoint", ex);
367  }
368  }
369 
376  private void showPopupMenu(MapWaypoint waypoint, Point point) throws TskCoreException {
377  if (waypoint == null) {
378  return;
379  }
380 
381  JMenuItem[] items = waypoint.getMenuItems();
382  JPopupMenu popupMenu = new JPopupMenu();
383  for (JMenuItem menu : items) {
384 
385  if (menu != null) {
386  popupMenu.add(menu);
387  } else {
388  popupMenu.add(new JSeparator());
389  }
390  }
391  popupMenu.show(mapViewer, point.x, point.y);
392  }
393 
397  private void showDetailsPopup() {
398  if (currentlySelectedWaypoint != null) {
399  if (currentPopup != null) {
400  currentPopup.hide();
401  }
402 
403  WaypointDetailPanel detailPane = new WaypointDetailPanel();
404  detailPane.setWaypoint(currentlySelectedWaypoint);
405  detailPane.setPreferredSize(new Dimension(POPUP_WIDTH, POPUP_HEIGHT));
406  detailPane.addActionListener(new ActionListener() {
407  @Override
408  public void actionPerformed(ActionEvent e) {
409  if (currentPopup != null) {
410  currentPopup.hide();
411  currentPopup = null;
412  }
413  }
414 
415  });
416 
417  Point popupLocation = getLocationForDetailsPopup();
418 
419  currentPopup = popupFactory.getPopup(this, detailPane, popupLocation.x, popupLocation.y);
420  currentPopup.show();
421 
422  mapViewer.revalidate();
423  mapViewer.repaint();
424  }
425  }
426 
432  private Point getLocationForDetailsPopup() {
433  Point panelCorner = this.getLocationOnScreen();
434  int width = this.getWidth();
435 
436  int popupX = panelCorner.x + width - POPUP_WIDTH - POPUP_MARGIN;
437  int popupY = panelCorner.y + POPUP_MARGIN;
438 
439  return new Point(popupX, popupY);
440  }
441 
450  private List<MapWaypoint> findClosestWaypoint(Point mouseClickPoint) {
451  if (waypointTree == null) {
452  return null;
453  }
454 
455  // Convert the mouse click location to latitude & longitude
456  GeoPosition geopos = mapViewer.getTileFactory().pixelToGeo(mouseClickPoint, mapViewer.getZoom());
457 
458  // Get the 5 nearest neightbors to the point
459  Collection<MapWaypoint> waypoints = waypointTree.nearestNeighbourSearch(10, MapWaypoint.getDummyWaypoint(geopos));
460 
461  if (waypoints == null || waypoints.isEmpty()) {
462  return null;
463  }
464 
465  Iterator<MapWaypoint> iterator = waypoints.iterator();
466 
467  // These maybe the points closest to lat/log was clicked but
468  // that doesn't mean they are close in terms of pixles.
469  List<MapWaypoint> closestPoints = new ArrayList<>();
470  while (iterator.hasNext()) {
471  MapWaypoint nextWaypoint = iterator.next();
472 
473  Point2D point = mapViewer.getTileFactory().geoToPixel(nextWaypoint.getPosition(), mapViewer.getZoom());
474 
475  Rectangle rect = mapViewer.getViewportBounds();
476  Point converted_gp_pt = new Point((int) point.getX() - rect.x,
477  (int) point.getY() - rect.y);
478 
479  if (converted_gp_pt.distance(mouseClickPoint) < 10) {
480  closestPoints.add(nextWaypoint);
481  }
482  }
483 
484  return closestPoints;
485  }
486 
496  public abstract class ComponentResizeEndListener
497  extends ComponentAdapter
498  implements ActionListener {
499 
500  private final Timer timer;
501  private static final int DEFAULT_TIMEOUT = 200;
502 
508  this(DEFAULT_TIMEOUT);
509  }
510 
516  public ComponentResizeEndListener(int delayMS) {
517  timer = new Timer(delayMS, this);
518  timer.setRepeats(false);
519  timer.setCoalesce(false);
520  }
521 
522  @Override
523  public void componentResized(ComponentEvent e) {
524  timer.restart();
525  }
526 
527  @Override
528  public void actionPerformed(ActionEvent e) {
529  resizeTimedOut();
530  }
531 
535  public abstract void resizeTimedOut();
536  }
537 
543  @SuppressWarnings("unchecked")
544  // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
545  private void initComponents() {
546  java.awt.GridBagConstraints gridBagConstraints;
547 
548  mapViewer = new org.jxmapviewer.JXMapViewer();
549  zoomPanel = new javax.swing.JPanel();
550  zoomSlider = new javax.swing.JSlider();
551 
552  setFocusable(false);
553  setLayout(new java.awt.BorderLayout());
554 
555  mapViewer.addMouseMotionListener(new java.awt.event.MouseMotionAdapter() {
556  public void mouseMoved(java.awt.event.MouseEvent evt) {
557  mapViewerMouseMoved(evt);
558  }
559  });
560  mapViewer.addMouseListener(new java.awt.event.MouseAdapter() {
561  public void mouseClicked(java.awt.event.MouseEvent evt) {
563  }
564  public void mousePressed(java.awt.event.MouseEvent evt) {
566  }
567  public void mouseReleased(java.awt.event.MouseEvent evt) {
569  }
570  });
571  mapViewer.setLayout(new java.awt.GridBagLayout());
572 
573  zoomPanel.setFocusable(false);
574  zoomPanel.setOpaque(false);
575  zoomPanel.setRequestFocusEnabled(false);
576 
577  zoomSlider.setMaximum(15);
578  zoomSlider.setMinimum(10);
579  zoomSlider.setMinorTickSpacing(1);
580  zoomSlider.setOrientation(javax.swing.JSlider.VERTICAL);
581  zoomSlider.setPaintTicks(true);
582  zoomSlider.setSnapToTicks(true);
583  zoomSlider.setMinimumSize(new java.awt.Dimension(35, 100));
584  zoomSlider.setOpaque(false);
585  zoomSlider.setPreferredSize(new java.awt.Dimension(35, 190));
586  zoomSlider.addChangeListener(new javax.swing.event.ChangeListener() {
587  public void stateChanged(javax.swing.event.ChangeEvent evt) {
589  }
590  });
591 
592  javax.swing.GroupLayout zoomPanelLayout = new javax.swing.GroupLayout(zoomPanel);
593  zoomPanel.setLayout(zoomPanelLayout);
594  zoomPanelLayout.setHorizontalGroup(
595  zoomPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
596  .addGroup(zoomPanelLayout.createSequentialGroup()
597  .addGap(0, 0, 0)
598  .addComponent(zoomSlider, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
599  .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
600  );
601  zoomPanelLayout.setVerticalGroup(
602  zoomPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
603  .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, zoomPanelLayout.createSequentialGroup()
604  .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
605  .addComponent(zoomSlider, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
606  .addGap(0, 0, 0))
607  );
608 
609  gridBagConstraints = new java.awt.GridBagConstraints();
610  gridBagConstraints.anchor = java.awt.GridBagConstraints.SOUTHWEST;
611  gridBagConstraints.weightx = 1.0;
612  gridBagConstraints.weighty = 1.0;
613  gridBagConstraints.insets = new java.awt.Insets(4, 4, 4, 4);
614  mapViewer.add(zoomPanel, gridBagConstraints);
615 
616  add(mapViewer, java.awt.BorderLayout.CENTER);
617  }// </editor-fold>//GEN-END:initComponents
618 
619  private void zoomSliderStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_zoomSliderStateChanged
620  if (!zoomChanging) {
621  setZoom(zoomSlider.getValue());
622  }
623  }//GEN-LAST:event_zoomSliderStateChanged
624 
625  private void mapViewerMousePressed(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_mapViewerMousePressed
626  if (evt.isPopupTrigger()) {
627  showPopupMenu(evt.getPoint());
628  }
629  }//GEN-LAST:event_mapViewerMousePressed
630 
631  private void mapViewerMouseReleased(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_mapViewerMouseReleased
632  if (evt.isPopupTrigger()) {
633  showPopupMenu(evt.getPoint());
634  }
635  }//GEN-LAST:event_mapViewerMouseReleased
636 
637  private void mapViewerMouseMoved(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_mapViewerMouseMoved
638  GeoPosition geopos = mapViewer.getTileFactory().pixelToGeo(evt.getPoint(), mapViewer.getZoom());
639  firePropertyChange(CURRENT_MOUSE_GEOPOSITION, null, geopos);
640  }//GEN-LAST:event_mapViewerMouseMoved
641 
642  private void mapViewerMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_mapViewerMouseClicked
643  if(!evt.isPopupTrigger() && SwingUtilities.isLeftMouseButton(evt)) {
644  List<MapWaypoint> waypoints = findClosestWaypoint(evt.getPoint());
645  if(waypoints.size() > 0) {
646  currentlySelectedWaypoint = waypoints.get(0);
647  }
648 
649 
650 // currentlySelectedWaypoint = findClosestWaypoint(evt.getPoint());
652  }
653  }//GEN-LAST:event_mapViewerMouseClicked
654 
655 
656  // Variables declaration - do not modify//GEN-BEGIN:variables
657  private org.jxmapviewer.JXMapViewer mapViewer;
658  private javax.swing.JPanel zoomPanel;
659  private javax.swing.JSlider zoomSlider;
660  // End of variables declaration//GEN-END:variables
661 
665  private class MapWaypointRenderer implements WaypointRenderer<Waypoint> {
666  private final BufferedImage defaultWaypointImage;
667  private final BufferedImage selectedWaypointImage;
668 
674  MapWaypointRenderer() throws IOException {
675  defaultWaypointImage = ImageIO.read(getClass().getResource("/org/sleuthkit/autopsy/images/waypoint_teal.png"));
676  selectedWaypointImage = ImageIO.read(getClass().getResource("/org/sleuthkit/autopsy/images/waypoint_yellow.png"));
677  }
678 
679  @Override
680  public void paintWaypoint(Graphics2D gd, JXMapViewer jxmv, Waypoint waypoint) {
681  Point2D point = jxmv.getTileFactory().geoToPixel(waypoint.getPosition(), jxmv.getZoom());
682 
683  int x = (int)point.getX();
684  int y = (int)point.getY();
685 
686  BufferedImage image = (waypoint == currentlySelectedWaypoint ? selectedWaypointImage: defaultWaypointImage);
687 
688  (gd.create()).drawImage(image, x -image.getWidth() / 2, y -image.getHeight(), null);
689  }
690 
691  }
692 }
void showPopupMenu(MapWaypoint waypoint, Point point)
Definition: MapPanel.java:376
void paintWaypoint(Graphics2D gd, JXMapViewer jxmv, Waypoint waypoint)
Definition: MapPanel.java:680
void zoomSliderStateChanged(javax.swing.event.ChangeEvent evt)
Definition: MapPanel.java:619
void mapViewerMouseReleased(java.awt.event.MouseEvent evt)
Definition: MapPanel.java:631
List< MapWaypoint > findClosestWaypoint(Point mouseClickPoint)
Definition: MapPanel.java:450
TileFactoryInfo createOnlineOSMFactory(String address)
Definition: MapPanel.java:270
void mapViewerMouseClicked(java.awt.event.MouseEvent evt)
Definition: MapPanel.java:642
void mapViewerMousePressed(java.awt.event.MouseEvent evt)
Definition: MapPanel.java:625
void mapViewerMouseMoved(java.awt.event.MouseEvent evt)
Definition: MapPanel.java:637
Collection< T > nearestNeighbourSearch(int K, T value)
Definition: KdTree.java:365
TileFactoryInfo createOSMZipFactory(String path)
Definition: MapPanel.java:288
static void error(String title, String message)
synchronized static Logger getLogger(String name)
Definition: Logger.java:124
static void addChangeListener(PreferenceChangeListener listener)
org.jxmapviewer.JXMapViewer mapViewer
Definition: MapPanel.java:657

Copyright © 2012-2019 Basis Technology. Generated on: Tue Jan 7 2020
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.