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