001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.plugins.streetside.mode;
003
004import java.awt.Cursor;
005import java.awt.Graphics2D;
006import java.awt.Point;
007import java.awt.event.MouseAdapter;
008import java.util.Calendar;
009
010import org.openstreetmap.josm.plugins.streetside.StreetsideAbstractImage;
011import org.openstreetmap.josm.plugins.streetside.StreetsideLayer;
012import org.openstreetmap.josm.plugins.streetside.io.download.StreetsideDownloader;
013
014import org.openstreetmap.josm.data.Bounds;
015import org.openstreetmap.josm.gui.MainApplication;
016import org.openstreetmap.josm.gui.MapView;
017import org.openstreetmap.josm.gui.NavigatableComponent.ZoomChangeListener;
018
019/**
020 * Superclass for all the mode of the {@link StreetsideLayer}.
021 *
022 * @author nokutu
023 * @see StreetsideLayer
024 */
025public abstract class AbstractMode extends MouseAdapter implements
026  ZoomChangeListener {
027
028  private static final int DOWNLOAD_COOLDOWN = 2000;
029  private static SemiautomaticThread semiautomaticThread = new SemiautomaticThread();
030
031  /**
032   * Cursor that should become active when this mode is activated.
033   */
034  public int cursor = Cursor.DEFAULT_CURSOR;
035
036  protected StreetsideAbstractImage getClosest(Point clickPoint) {
037    double snapDistance = 10;
038    double minDistance = Double.MAX_VALUE;
039    StreetsideAbstractImage closest = null;
040    for (StreetsideAbstractImage image : StreetsideLayer.getInstance().getData().getImages()) {
041      Point imagePoint = MainApplication.getMap().mapView.getPoint(image.getMovingLatLon());
042      imagePoint.setLocation(imagePoint.getX(), imagePoint.getY());
043      double dist = clickPoint.distanceSq(imagePoint);
044      if (minDistance > dist && clickPoint.distance(imagePoint) < snapDistance
045        && image.isVisible()) {
046        minDistance = dist;
047        closest = image;
048      }
049    }
050    return closest;
051  }
052
053  /**
054   * Paint the dataset using the engine set.
055   *
056   * @param g {@link Graphics2D} used for painting
057   * @param mv The object that can translate GeoPoints to screen coordinates.
058   * @param box Area where painting is going to be performed
059   */
060  public abstract void paint(Graphics2D g, MapView mv, Bounds box);
061
062  @Override
063  public void zoomChanged() {
064    if (StreetsideDownloader.getMode() == StreetsideDownloader.DOWNLOAD_MODE.VISIBLE_AREA) {
065      if (!semiautomaticThread.isAlive())
066        semiautomaticThread.start();
067      semiautomaticThread.moved();
068    }
069  }
070
071  /**
072   * Resets the semiautomatic mode thread.
073   */
074  public static void resetThread() {
075    semiautomaticThread.interrupt();
076    semiautomaticThread = new SemiautomaticThread();
077  }
078
079  private static class SemiautomaticThread extends Thread {
080
081    /** If in semiautomatic mode, the last Epoch time when there was a download */
082    private long lastDownload;
083
084    private boolean moved;
085
086    @Override
087    public void run() {
088      while (true) {
089        if (this.moved && Calendar.getInstance().getTimeInMillis() - this.lastDownload >= DOWNLOAD_COOLDOWN) {
090          this.lastDownload = Calendar.getInstance().getTimeInMillis();
091          StreetsideDownloader.downloadVisibleArea();
092          this.moved = false;
093          StreetsideLayer.invalidateInstance();
094        }
095        try {
096          Thread.sleep(100);
097        } catch (InterruptedException e) {
098          return;
099        }
100      }
101    }
102
103    public void moved() {
104      this.moved = true;
105    }
106  }
107}