Package aimax.osm.viewer

Source Code of aimax.osm.viewer.MapViewPane

package aimax.osm.viewer;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Toolkit;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JComponent;
import javax.swing.JOptionPane;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTable;

import aimax.osm.data.BoundingBox;
import aimax.osm.data.MapEvent;
import aimax.osm.data.MapEventListener;
import aimax.osm.data.OsmMap;
import aimax.osm.data.Position;
import aimax.osm.data.entities.EntityAttribute;
import aimax.osm.data.entities.MapEntity;
import aimax.osm.data.entities.MapNode;
import aimax.osm.data.entities.WayRef;
import aimax.osm.data.impl.DefaultMap;

/**
* Provides a panel which shows map data. As model, a
* {@link aimax.osm.data.OsmMap} is used.
* <p>
* Hint for using the viewer: Try Mouse-Left, Mouse-Right, Mouse-Drag,
* Ctrl-Mouse-Left, Plus, Minus, Ctrl-Plus, Ctrl-Minus, arrow buttons, and also
* the Mouse-Wheel for navigation, mark setting, and track definition.
*
* @author Ruediger Lunde
*/
public class MapViewPane extends JComponent implements MapEventListener {

  private static final long serialVersionUID = 1L;
  // private Logger LOG = Logger.getLogger("aimax.osm");

  /**
   * Maintains a reference to the model which provides the data to be
   * displayed.
   */
  protected OsmMap map;
  protected CoordTransformer transformer;
  private AbstractEntityRenderer renderer;
  private ArrayList<MapViewEventListener> eventListeners;
  protected boolean isAdjusted;
  protected JPopupMenu popup;

  public MapViewPane() {
    transformer = new CoordTransformer();
    transformer.setScreenResolution(Toolkit.getDefaultToolkit()
        .getScreenResolution()); // doesn't work...
    renderer = new DefaultEntityRenderer();
    eventListeners = new ArrayList<MapViewEventListener>();
    isAdjusted = false;
    popup = new MapViewPopup();
    MyMouseListener mouseListener = new MyMouseListener();
    addMouseListener(mouseListener);
    addMouseWheelListener(mouseListener);
    addKeyListener(new MyKeyListener());
    this.setFocusable(true);
  }

  public OsmMap getMap() {
    return map;
  }

  /** Sets the map as model of this pane and initiates painting. */
  public void setMap(OsmMap map) {
    if (this.map != null)
      this.map.removeMapDataEventListener(this);
    this.map = map;
    if (map != null) {
      map.addMapDataEventListener(this);
      isAdjusted = false;
    }
    fireMapViewEvent(new MapViewEvent(this, MapViewEvent.Type.MAP_NEW));
  }

  public AbstractEntityRenderer getRenderer() {
    return renderer;
  }

  /** Allows to replace the renderer. */
  public void setRenderer(AbstractEntityRenderer renderer) {
    this.renderer = renderer;
    repaint();
  }

  /** Returns the component responsible for coordinate transformation. */
  public CoordTransformer getTransformer() {
    return transformer;
  }

  /** Changes the pop-up menu shown for mouse-right. */
  public void setPopupMenu(JPopupMenu popup) {
    this.popup = popup;
  }

  /**
   * Provides the true width of the screen to the transformer. This is
   * necessary to get correct scale values.
   *
   * @param cm
   *            Screen width in centimeters.
   */
  public void setScreenWidthInCentimeter(double cm) {
    double dotsPerCm = Toolkit.getDefaultToolkit().getScreenSize()
        .getWidth()
        / cm;
    transformer.setScreenResolution((int) (dotsPerCm * 2.54));
  }

  /**
   * Provides the true size of the screen to the transformer. This is
   * necessary to get correct scale values.
   *
   * @param inch
   *            Screen size in inch.
   */
  public void setScreenSizeInInch(double inch) {
    double width = Toolkit.getDefaultToolkit().getScreenSize().getWidth();
    double height = Toolkit.getDefaultToolkit().getScreenSize().getHeight();
    double dotsPerInch = Math.sqrt(width * width + height * height) / inch;
    transformer.setScreenResolution((int) dotsPerInch);
  }

  /**
   * Multiples the current scale with the specified factor and adjusts the
   * view so that the objects shown at the specified view focus keep at their
   * position.
   */
  public void zoom(float factor, int focusX, int focusY) {
    transformer.zoom(factor, focusX, focusY);
    repaint();
    fireMapViewEvent(new MapViewEvent(this, MapViewEvent.Type.ZOOM));
  }

  public void multiplyDisplayFactorWith(float fac) {
    renderer.setDisplayFactor(renderer.getDisplayFactor() * fac);
    repaint();
    fireMapViewEvent(new MapViewEvent(this, MapViewEvent.Type.ZOOM));
  }

  /**
   * Adjusts the view.
   *
   * @param dx
   *            Number of pixels for horizontal shift.
   * @param dy
   *            Number of pixels for vertical shift.
   */
  public void adjust(int dx, int dy) {
    transformer.adjust(dx, dy);
    repaint();
    fireMapViewEvent(new MapViewEvent(this, MapViewEvent.Type.ADJUST));
  }

  /**
   * Initiates a reset of all coordinate transformation parameters.
   */
  public void adjustToFit() {
    isAdjusted = false;
    repaint();
    fireMapViewEvent(new MapViewEvent(this, MapViewEvent.Type.ADJUST));
  }

  /**
   * Puts a position given in world coordinates into the center of the view.
   *
   * @param lat
   *            Latitude
   * @param lon
   *            Longitude
   */
  public void adjustToCenter(double lat, double lon) {
    int dx = getWidth() / 2 - transformer.x(lon);
    int dy = getHeight() / 2 - transformer.y(lat);
    adjust(dx, dy);
  }

  /**
   * Returns the world coordinates, which are currently shown in the center.
   */
  public Position getCenterPosition() {
    float lat = transformer.lat(getHeight() / 2);
    float lon = transformer.lon(getWidth() / 2);
    return new Position(lat, lon);
  }

  /** Returns a bounding box describing the currently visible area. */
  public BoundingBox getBoundingBox() {
    float latMin = transformer.lat(getHeight());
    float lonMin = transformer.lon(0);
    float latMax = transformer.lat(0);
    float lonMax = transformer.lon(getWidth());
    return new BoundingBox(latMin, lonMin, latMax, lonMax);
  }

  /**
   * Removes the mark which is the nearest with respect to the given view
   * coordinates.
   */
  public void removeNearestMarker(int x, int y) {
    List<MapNode> marks = map.getMarkers();
    float lat = getTransformer().lat(y);
    float lon = getTransformer().lon(x);
    MapNode mark = new Position(lat, lon).selectNearest(marks, null);
    if (mark != null)
      marks.remove(mark);
    map.fireMapDataEvent(new MapEvent(map,
        MapEvent.Type.MAP_MODIFIED));
    fireMapViewEvent(new MapViewEvent(this,
        MapViewEvent.Type.TMP_NODES_REMOVED));
  }

  /**
   * Finds the visible entity next to the specified view coordinates and shows
   * informations about it.
   *
   * @param debug
   *            Enables a more detailed view.
   */
  public void showMapEntityInfoDialog(MapEntity entity, boolean debug) {
    List<MapEntity> entities = new ArrayList<MapEntity>();
    if (entity.getName() != null || entity.getAttributes().length > 0
        || debug)
      entities.add(entity);
    if (entity instanceof MapNode) {
      MapNode mNode = (MapNode) entity;
      for (WayRef ref : mNode.getWayRefs()) {
        MapEntity me = ref.getWay();
        if (me.getName() != null || me.getAttributes().length > 0
            || debug)
          entities.add(me);
      }
    }
    boolean done = false;
    for (int i = 0; i < entities.size() && !done; i++) {
      MapEntity me = entities.get(i);
      Object[] content = new Object[] { "", "", "" };
      String text = (me.getName() != null) ? me.getName() : "";
      if (debug)
        text += " (" + ((me instanceof MapNode) ? "Node " : "Way ")
            + me.getId() + ")";
      content[0] = text;
      if (me instanceof MapNode) {
        PositionPanel pos = new PositionPanel();
        pos.setPosition(((MapNode) me).getLat(), ((MapNode) me)
            .getLon());
        pos.setEnabled(false);
        content[1] = pos;
      }
      if (me.getAttributes().length > 0) {
        EntityAttribute[] atts = me.getAttributes();
        String[][] attTexts = new String[atts.length][2];
        for (int j = 0; j < atts.length; j++) {
          attTexts[j][0] = atts[j].getKey();
          attTexts[j][1] = atts[j].getValue();
        }
        JTable table = new JTable(attTexts, new String[] { "Name",
            "Value" });
        JScrollPane spane = new JScrollPane(table);
        spane.setPreferredSize(new Dimension(300, 150));
        content[2] = spane;
      }
      Object[] options;
      if (i < entities.size() - 1)
        options = new String[] { "OK", "Next" };
      else
        options = new String[] { "OK" };
      if (JOptionPane.showOptionDialog(this, content, "Map Entity Info",
          JOptionPane.DEFAULT_OPTION,
          JOptionPane.INFORMATION_MESSAGE, null, options, options[0]) != 1)
        done = true;
    }
  }

  /**
   * Shows a graphical representation of the provided map data.
   */
  public void paint(Graphics g) {
    Graphics2D g2 = (java.awt.Graphics2D) g;
    g2.setBackground(renderer.getBackgroundColor());
    g2.clearRect(0, 0, getWidth(), getHeight());
    if (getWidth() > 0 && map != null) {
      if (!isAdjusted) {
        transformer.adjustTransformation(map.getBoundingBox(),
            getWidth(), getHeight());
        isAdjusted = true;
      }
      float latMin = transformer.lat(getHeight());
      float lonMin = transformer.lon(0);
      float latMax = transformer.lat(0);
      float lonMax = transformer.lon(getWidth());
      float scale = transformer.computeScale();
      BoundingBox vbox = new BoundingBox(latMin, lonMin, latMax, lonMax);
      float viewScale = scale / renderer.getDisplayFactor();
      renderer.initForRendering(g2, transformer, map);
      map.visitEntities(renderer, vbox, viewScale);
      for (MapEntity entity : map.getVisibleMarkersAndTracks(viewScale))
        entity.accept(renderer);
      renderer.printBufferedObjects();
      if (renderer.isDebugModeEnabled()
          && map instanceof DefaultMap) {
        List<double[]> splits = ((DefaultMap) map)
            .getEntityTree().getSplitCoords();
        g2.setColor(Color.LIGHT_GRAY);
        g2.setStroke(new BasicStroke(1f));
        for (double[] split : splits)
          g2.drawLine(renderer.transformer.x(split[1]),
              renderer.transformer.y(split[0]),
              renderer.transformer.x(split[3]),
              renderer.transformer.y(split[2]));
      }
    }
  }

  public void addMapViewEventListener(MapViewEventListener listener) {
    eventListeners.add(listener);
  }

  /**
   * Informs all interested listener about view events such as mouse events
   * and data changes.
   */
  public void fireMapViewEvent(MapViewEvent e) {
    for (MapViewEventListener listener : eventListeners)
      listener.eventHappened(e);
  }

  /**
   * Defines, how to react on model events (new, modifications).
   */
  @Override
  public void eventHappened(MapEvent event) {
    if (event.getType() == MapEvent.Type.MAP_NEW) {
      adjustToFit();
      fireMapViewEvent(new MapViewEvent(this, MapViewEvent.Type.MAP_NEW));
    } else {
      repaint();
    }
  }

  // ////////////////////////////////////////////////////////////////////
  // some inner classes...

  /**
   * Defines reactions on mouse events including navigation, mark setting and
   * track creation.
   *
   * @author R. Lunde
   *
   */
  private class MyMouseListener implements MouseListener, MouseWheelListener {
    int xp;
    int yp;
    MapNode mark;

    @Override
    public void mouseClicked(MouseEvent e) {
      if (e.getButton() == MouseEvent.BUTTON1) {
        float lat = transformer.lat(e.getY());
        float lon = transformer.lon(e.getX());
        if ((e.getModifiers() & MouseEvent.CTRL_MASK) != 0) {
          // mouse left button + ctrl -> add track point
          map.addToTrack("Mouse Track",
              new Position(lat, lon));
          fireMapViewEvent(new MapViewEvent(MapViewPane.this,
              MapViewEvent.Type.TRK_PT_ADDED));
        } else if ((e.getModifiers() & MouseEvent.SHIFT_MASK) != 0) {
          // mouse left button + shift -> add track point
          removeNearestMarker(e.getX(), e.getY());
        } else if (e.getClickCount() == 1) {
          // mouse left button -> add mark
          mark = map.addMarker(lat, lon);
          fireMapViewEvent(new MapViewEvent(MapViewPane.this,
              MapViewEvent.Type.MARKER_ADDED));
        } else { // double click
          map.removeMarker(mark);
          MapNode mNode = getRenderer().getNextNode(e.getX(), e.getY());
          if (mNode != null)
            showMapEntityInfoDialog(mNode, renderer.isDebugModeEnabled());
        }
      } else if (popup != null) {
        popup.show(MapViewPane.this, e.getX(), e.getY());
      } else {
        // mouse right button -> clear marks and tracks
        map.clearMarkersAndTracks();
        fireMapViewEvent(new MapViewEvent(MapViewPane.this,
            MapViewEvent.Type.TMP_NODES_REMOVED));
      }
    }

    @Override
    public void mouseEntered(MouseEvent arg0) {
      MapViewPane.this.requestFocusInWindow();
    }

    @Override
    public void mouseExited(MouseEvent arg0) {
    }

    @Override
    public void mousePressed(MouseEvent e) {
      xp = e.getX();
      yp = e.getY();
    }

    @Override
    public void mouseReleased(MouseEvent e) {
      if (e.getButton() == MouseEvent.BUTTON1) {
        int xr = e.getX();
        int yr = e.getY();
        if (xr != xp || yr != yp) {
          // mouse drag -> adjust view
          adjust(xr - xp, yr - yp);
        }
      }
    }

    @Override
    public void mouseWheelMoved(MouseWheelEvent e) {
      int rot = e.getWheelRotation();
      int x = e.getX();
      int y = e.getY();
      float fac = ((e.getModifiers() & KeyEvent.SHIFT_MASK) != 0) ? 1.1f
          : 1.5f;
      if (rot == -1) {
        if ((e.getModifiers() & KeyEvent.ALT_MASK) != 0) {
          multiplyDisplayFactorWith(fac);
        } else
          zoom(fac, x, y);
      } else if (rot == 1) {
        if ((e.getModifiers() & KeyEvent.ALT_MASK) != 0) {
          multiplyDisplayFactorWith(1f / fac);
        } else
          zoom(1 / fac, x, y);
      }
    }
  }

  /**
   * Enables map navigation (zooming, adjustment) with the keyboard.
   *
   * @author R. Lunde
   */
  private class MyKeyListener implements KeyListener {

    @Override
    public void keyPressed(KeyEvent e) {
      float zfac = ((e.getModifiers() & KeyEvent.SHIFT_MASK) != 0) ? 1.1f
          : 1.5f;
      float afac = ((e.getModifiers() & KeyEvent.SHIFT_MASK) != 0) ? 0.1f
          : 0.3f;
      switch (e.getKeyCode()) {
      case KeyEvent.VK_PLUS:
        if ((e.getModifiers() & KeyEvent.ALT_MASK) != 0)
          multiplyDisplayFactorWith(zfac);
        else
          zoom(zfac, getWidth() / 2, getHeight() / 2);
        break;
      case KeyEvent.VK_MINUS:
        if ((e.getModifiers() & KeyEvent.ALT_MASK) != 0)
          multiplyDisplayFactorWith(1f / zfac);
        else
          zoom(1 / zfac, getWidth() / 2, getHeight() / 2);
        break;
      case KeyEvent.VK_SPACE:
        if ((e.getModifiers() & KeyEvent.CTRL_MASK) == 0)
          zoom(zfac, getWidth() / 2, getHeight() / 2);
        else
          zoom(1 / zfac, getWidth() / 2, getHeight() / 2);
        break;
      case KeyEvent.VK_LEFT:
        adjust((int) (afac * getWidth()), 0);
        break;
      case KeyEvent.VK_RIGHT:
        adjust((int) (-afac * getWidth()), 0);
        break;
      case KeyEvent.VK_UP:
        adjust(0, (int) (afac * getHeight()));
        break;
      case KeyEvent.VK_DOWN:
        adjust(0, (int) (-afac * getHeight()));
        break;
      }
    }

    @Override
    public void keyReleased(KeyEvent e) {
    }

    @Override
    public void keyTyped(KeyEvent e) {
    }
  }
}
TOP

Related Classes of aimax.osm.viewer.MapViewPane

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.