Ignore:
Timestamp:
2015-12-28T14:23:04+01:00 (9 years ago)
Author:
nokutu
Message:
  • Changed database ArrayList to ConcurrentSkipListSet, in order to improve performance.
  • When hovering an image whose full resolution picture has already been cached, the full resolution picture is shown instead of the thumbnail.
Location:
applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary
Files:
22 edited

Legend:

Unmodified
Added
Removed
  • applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/MapillaryAbstractImage.java

    r31812 r31882  
    1717 *
    1818 */
    19 public class MapillaryAbstractImage {
     19public class MapillaryAbstractImage implements Comparable<MapillaryAbstractImage>{
    2020  /**
    2121   * If two values for field ca differ by less than EPSILON both values are considered equal.
     
    120120    final Date date = new Date(getCapturedAt());
    121121
    122     final SimpleDateFormat formatter = new SimpleDateFormat(format, Locale.UK);
     122    final SimpleDateFormat formatter = new SimpleDateFormat(format);
    123123    formatter.setTimeZone(Calendar.getInstance().getTimeZone());
    124124    return formatter.format(date);
     
    267267    this.movingCa = this.tempCa + ca;
    268268  }
     269
     270  @Override
     271  public int compareTo(MapillaryAbstractImage mapillaryAbstractImage) {
     272    return hashCode() - mapillaryAbstractImage.hashCode();
     273  }
    269274}
  • applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/MapillaryData.java

    r31833 r31882  
    22package org.openstreetmap.josm.plugins.mapillary;
    33
    4 import java.util.ArrayList;
    5 import java.util.List;
     4import java.util.*;
     5import java.util.concurrent.ConcurrentHashMap;
     6import java.util.concurrent.ConcurrentSkipListSet;
    67import java.util.concurrent.CopyOnWriteArrayList;
    78
     
    1718 * @see MapillaryAbstractImage
    1819 * @see MapillarySequence
    19  *
    2020 */
    2121public class MapillaryData {
    2222
    23   private List<MapillaryAbstractImage> images;
    24   /** The image currently selected, this is the one being shown. */
     23  private Set<MapillaryAbstractImage> images;
     24  /**
     25   * The image currently selected, this is the one being shown.
     26   */
    2527  private MapillaryAbstractImage selectedImage;
    26   /** The image under the cursor. */
     28  /**
     29   * The image under the cursor.
     30   */
    2731  private MapillaryAbstractImage highlightedImage;
    28   /** All the images selected, can be more than one. */
    29   private final List<MapillaryAbstractImage> multiSelectedImages;
    30   /** Listeners of the class. */
     32  /**
     33   * All the images selected, can be more than one.
     34   */
     35  private final Set<MapillaryAbstractImage> multiSelectedImages;
     36  /**
     37   * Listeners of the class.
     38   */
    3139  private final CopyOnWriteArrayList<MapillaryDataListener> listeners = new CopyOnWriteArrayList<>();
    32   /** The bounds of the areas for which the pictures have been downloaded. */
     40  /**
     41   * The bounds of the areas for which the pictures have been downloaded.
     42   */
    3343  public List<Bounds> bounds;
    3444
     
    3747   */
    3848  protected MapillaryData() {
    39     this.images = new CopyOnWriteArrayList<>();
    40     this.multiSelectedImages = new ArrayList<>();
     49    this.images = new ConcurrentSkipListSet<>();
     50    this.multiSelectedImages = new ConcurrentSkipListSet<>();
    4151    this.selectedImage = null;
    4252
     
    5262   * Adds an MapillaryImage to the object, and then repaints mapView.
    5363   *
    54    * @param image
    55    *          The image to be added.
     64   * @param image The image to be added.
    5665   */
    5766  public synchronized void add(MapillaryAbstractImage image) {
     
    6372   * needed for concurrency.
    6473   *
    65    * @param image
    66    *          The image to be added.
    67    * @param update
    68    *          Whether the map must be updated or not.
     74   * @param image  The image to be added.
     75   * @param update Whether the map must be updated or not.
    6976   */
    7077  public synchronized void add(MapillaryAbstractImage image, boolean update) {
     
    8087   * Adds a set of MapillaryImages to the object, and then repaints mapView.
    8188   *
    82    * @param images
    83    *          The set of images to be added.
    84    */
    85   public synchronized void add(List<MapillaryAbstractImage> images) {
     89   * @param images The set of images to be added.
     90   */
     91  public synchronized void add(Set<MapillaryAbstractImage> images) {
    8692    add(images, true);
    8793  }
     
    9096   * Adds a set of {link MapillaryAbstractImage} objects to this object.
    9197   *
    92    * @param images
    93    *          The set of images to be added.
    94    * @param update
    95    *          Whether the map must be updated or not.
    96    */
    97   public synchronized void add(List<MapillaryAbstractImage> images, boolean update) {
     98   * @param images The set of images to be added.
     99   * @param update Whether the map must be updated or not.
     100   */
     101  public synchronized void add(Set<MapillaryAbstractImage> images, boolean update) {
    98102    for (MapillaryAbstractImage image : images) {
    99103      add(image, update);
     
    104108   * Adds a new listener.
    105109   *
    106    * @param lis
    107    *          Listener to be added.
     110   * @param lis Listener to be added.
    108111   */
    109112  public void addListener(MapillaryDataListener lis) {
     
    115118   * ctrl + click)
    116119   *
    117    * @param image
    118    *          The {@link MapillaryImage} object to be added.
     120   * @param image The {@link MapillaryImage} object to be added.
    119121   */
    120122  public void addMultiSelectedImage(MapillaryAbstractImage image) {
     
    133135   * selected images.
    134136   *
    135    * @param images
    136    *          A List object containing the set of images to be added.
    137    */
    138   public void addMultiSelectedImage(List<MapillaryAbstractImage> images) {
     137   * @param images A List object containing the set of images to be added.
     138   */
     139  public void addMultiSelectedImage(Set<MapillaryAbstractImage> images) {
    139140    for (MapillaryAbstractImage image : images)
    140141      if (!this.multiSelectedImages.contains(image)) {
     
    151152   * and from its {@link MapillarySequence}.
    152153   *
    153    * @param image
    154    *          The {@link MapillaryAbstractImage} that is going to be deleted.
     154   * @param image The {@link MapillaryAbstractImage} that is going to be deleted.
    155155   */
    156156  public synchronized void remove(MapillaryAbstractImage image) {
    157157    if (Main.main != null
    158         && MapillaryMainDialog.getInstance().getImage() != null) {
     158            && MapillaryMainDialog.getInstance().getImage() != null) {
    159159      MapillaryMainDialog.getInstance().setImage(null);
    160160      MapillaryMainDialog.getInstance().updateImage();
     
    171171   * Removes a set of images from the database.
    172172   *
    173    * @param images
    174    *          A {@link List} of {@link MapillaryAbstractImage} objects that are
    175    *          going to be removed.
    176    */
    177   public synchronized void remove(List<MapillaryAbstractImage> images) {
    178     for (MapillaryAbstractImage img : images)
     173   * @param images A {@link List} of {@link MapillaryAbstractImage} objects that are
     174   *               going to be removed.
     175   */
     176  public synchronized void remove(Set<MapillaryAbstractImage> images) {
     177    for (MapillaryAbstractImage img : images) {
    179178      remove(img);
     179    }
    180180  }
    181181
     
    183183   * Removes a listener.
    184184   *
    185    * @param lis
    186    *          Listener to be removed.
     185   * @param lis Listener to be removed.
    187186   */
    188187  public void removeListener(MapillaryDataListener lis) {
     
    193192   * Highlights the image under the cursor.
    194193   *
    195    * @param image
    196    *          The image under the cursor.
     194   * @param image The image under the cursor.
    197195   */
    198196  public void setHighlightedImage(MapillaryAbstractImage image) {
     
    222220   * @return A List object containing all images.
    223221   */
    224   public synchronized List<MapillaryAbstractImage> getImages() {
     222  public synchronized Set<MapillaryAbstractImage> getImages() {
    225223    return this.images;
    226224  }
     
    248246   * nothing.
    249247   *
    250    * @throws IllegalStateException
    251    *           if the selected image is null or the selected image doesn't
    252    *           belong to a sequence.
     248   * @throws IllegalStateException if the selected image is null or the selected image doesn't
     249   *                               belong to a sequence.
    253250   */
    254251  public void selectNext() {
     
    261258   * nothing.
    262259   *
    263    * @param moveToPicture
    264    *          True if the view must me moved to the next picture.
    265    * @throws IllegalStateException
    266    *           if the selected image is null or the selected image doesn't
    267    *           belong to a sequence.
     260   * @param moveToPicture True if the view must me moved to the next picture.
     261   * @throws IllegalStateException if the selected image is null or the selected image doesn't
     262   *                               belong to a sequence.
    268263   */
    269264  public void selectNext(boolean moveToPicture) {
     
    287282   * nothing.
    288283   *
    289    * @throws IllegalStateException
    290    *           if the selected image is null or the selected image doesn't
    291    *           belong to a sequence.
     284   * @throws IllegalStateException if the selected image is null or the selected image doesn't
     285   *                               belong to a sequence.
    292286   */
    293287  public void selectPrevious() {
     
    301295   * the selected image doesn't belong to a sequence.
    302296   *
    303    * @param moveToPicture
    304    *          True if the view must me moved to the previous picture.
    305    * @throws IllegalStateException
    306    *           if the selected image is null or the selected image doesn't
    307    *           belong to a sequence.
     297   * @param moveToPicture True if the view must me moved to the previous picture.
     298   * @throws IllegalStateException if the selected image is null or the selected image doesn't
     299   *                               belong to a sequence.
    308300   */
    309301  public void selectPrevious(boolean moveToPicture) {
     
    325317   * Selects a new image.If the user does ctrl + click, this isn't triggered.
    326318   *
    327    * @param image
    328    *          The MapillaryImage which is going to be selected
    329    *
     319   * @param image The MapillaryImage which is going to be selected
    330320   */
    331321  public void setSelectedImage(MapillaryAbstractImage image) {
     
    337327   * can choose whether to center the view on the new image or not.
    338328   *
    339    * @param image
    340    *          The {@link MapillaryImage} which is going to be selected.
    341    * @param zoom
    342    *          True if the view must be centered on the image; false otherwise.
     329   * @param image The {@link MapillaryImage} which is going to be selected.
     330   * @param zoom  True if the view must be centered on the image; false otherwise.
    343331   */
    344332  public void setSelectedImage(MapillaryAbstractImage image, boolean zoom) {
     
    346334    this.selectedImage = image;
    347335    this.multiSelectedImages.clear();
    348     this.multiSelectedImages.add(image);
     336    if (image != null) {
     337      this.multiSelectedImages.add(image);
     338    }
    349339    if (image != null && Main.main != null && image instanceof MapillaryImage) {
    350340      MapillaryImage mapillaryImage = (MapillaryImage) image;
     
    369359
    370360  private void fireSelectedImageChanged(MapillaryAbstractImage oldImage,
    371       MapillaryAbstractImage newImage) {
     361                                        MapillaryAbstractImage newImage) {
    372362    if (this.listeners.isEmpty())
    373363      return;
     
    383373   * @return A List object containing all the images selected.
    384374   */
    385   public List<MapillaryAbstractImage> getMultiSelectedImages() {
     375  public Set<MapillaryAbstractImage> getMultiSelectedImages() {
    386376    return this.multiSelectedImages;
    387377  }
     
    392382   * @param images the new image list (previously set images are completely replaced)
    393383   */
    394   public synchronized void setImages(List<MapillaryAbstractImage> images) {
    395     this.images = new ArrayList<>(images);
     384  public synchronized void setImages(Set<MapillaryAbstractImage> images) {
     385    this.images = new ConcurrentSkipListSet<>(images);
    396386  }
    397387
  • applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/MapillaryImage.java

    r31811 r31882  
    120120
    121121  @Override
     122  public int compareTo(MapillaryAbstractImage image) {
     123    if (image instanceof MapillaryImage)
     124      return this.key.compareTo(((MapillaryImage) image).getKey());
     125    return super.compareTo(image);
     126  }
     127
     128  @Override
    122129  public int hashCode() {
    123130    return this.key.hashCode();
  • applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/MapillaryImportedImage.java

    r31842 r31882  
    100100
    101101  @Override
     102  public int compareTo(MapillaryAbstractImage image) {
     103    if (image instanceof MapillaryImage)
     104      return this.file.compareTo(((MapillaryImportedImage) image).getFile());
     105    return super.compareTo(image);
     106  }
     107
     108  @Override
    102109  public int hashCode() {
    103110    return this.file.hashCode();
  • applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/MapillarySequence.java

    r31826 r31882  
    1010 * @author nokutu
    1111 * @see MapillaryAbstractImage
    12  *
    1312 */
    1413public class MapillarySequence {
    15   /** The images in the sequence. */
     14  /**
     15   * The images in the sequence.
     16   */
    1617  private final List<MapillaryAbstractImage> images;
    17   /** Unique identifier. Used only for {@link MapillaryImage} sequences. */
     18  /**
     19   * Unique identifier. Used only for {@link MapillaryImage} sequences.
     20   */
    1821  private String key;
    19   /** Epoch time when the sequence was created */
     22  /**
     23   * Epoch time when the sequence was created
     24   */
    2025  private long createdAt;
    2126
     
    3136   * Creates a sequence object with the given parameters.
    3237   *
    33    * @param key
    34    *          The unique identifier of the sequence.
    35    * @param createdAt
    36    *          The date the sequence was created.
     38   * @param key       The unique identifier of the sequence.
     39   * @param createdAt The date the sequence was created.
    3740   */
    3841  public MapillarySequence(String key, long createdAt) {
     
    4548   * Adds a new {@link MapillaryAbstractImage} object to the database.
    4649   *
    47    * @param image
    48    *          The {@link MapillaryAbstractImage} object to be added
     50   * @param image The {@link MapillaryAbstractImage} object to be added
    4951   */
    5052  public synchronized void add(MapillaryAbstractImage image) {
     
    5557   * Adds a set of {@link MapillaryAbstractImage} objects to the database.
    5658   *
    57    * @param images
    58    *          The set of {@link MapillaryAbstractImage} objects to be added.
     59   * @param images The set of {@link MapillaryAbstractImage} objects to be added.
    5960   */
    6061  public synchronized void add(List<MapillaryAbstractImage> images) {
     
    6768   *
    6869   * @return A long containing the Epoch time when the sequence was captured.
    69    *
    7070   */
    7171  public long getCreatedAt() {
     
    7878   *
    7979   * @return A {@link List} object containing all the
    80    *         {@link MapillaryAbstractImage} objects that are part of the
    81    *         sequence.
     80   * {@link MapillaryAbstractImage} objects that are part of the
     81   * sequence.
    8282   */
    8383  public List<MapillaryAbstractImage> getImages() {
     
    8989   *
    9090   * @return A {@code String} containing the unique identifier of the sequence.
    91    *         null means that the sequence has been created locally for imported
    92    *         images.
     91   * null means that the sequence has been created locally for imported
     92   * images.
    9393   */
    9494  public String getKey() {
     
    100100   * {@link MapillaryAbstractImage} object.
    101101   *
    102    * @param image
    103    *          The {@link MapillaryAbstractImage} object whose next image is
    104    *          going to be returned.
     102   * @param image The {@link MapillaryAbstractImage} object whose next image is
     103   *              going to be returned.
    105104   * @return The next {@link MapillaryAbstractImage} object in the sequence.
    106    * @throws IllegalArgumentException
    107    *           if the given {@link MapillaryAbstractImage} object doesn't belong
    108    *           the this sequence.
     105   * @throws IllegalArgumentException if the given {@link MapillaryAbstractImage} object doesn't belong
     106   *                                  the this sequence.
    109107   */
    110108  public MapillaryAbstractImage next(MapillaryAbstractImage image) {
    111     if (!this.images.contains(image))
     109    int i = this.images.indexOf(image);
     110    if (i == -1) {
    112111      throw new IllegalArgumentException();
    113     int i = this.images.indexOf(image);
    114     if (i == this.images.size() - 1)
     112    }
     113    if (i == this.images.size() - 1) {
    115114      return null;
     115    }
    116116    return this.images.get(i + 1);
    117117  }
     
    121121   * given {@link MapillaryAbstractImage} object.
    122122   *
    123    * @param image
    124    *          The {@link MapillaryAbstractImage} object whose previous image is
    125    *          going to be returned.
     123   * @param image The {@link MapillaryAbstractImage} object whose previous image is
     124   *              going to be returned.
    126125   * @return The previous {@link MapillaryAbstractImage} object in the sequence.
    127    * @throws IllegalArgumentException
    128    *           if the given {@link MapillaryAbstractImage} object doesn't belong
    129    *           the this sequence.
     126   * @throws IllegalArgumentException if the given {@link MapillaryAbstractImage} object doesn't belong
     127   *                                  the this sequence.
    130128   */
    131129  public MapillaryAbstractImage previous(MapillaryAbstractImage image) {
    132     if (!this.images.contains(image))
     130    int i = this.images.indexOf(image);
     131    if (i == -1) {
    133132      throw new IllegalArgumentException();
    134     int i = this.images.indexOf(image);
    135     if (i == 0)
     133    }
     134    if (i == 0) {
    136135      return null;
     136    }
    137137    return this.images.get(i - 1);
    138138  }
     
    141141   * Removes a {@link MapillaryAbstractImage} object from the database.
    142142   *
    143    * @param image
    144    *          The {@link MapillaryAbstractImage} object to be removed.
     143   * @param image The {@link MapillaryAbstractImage} object to be removed.
    145144   */
    146145  public void remove(MapillaryAbstractImage image) {
  • applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/actions/MapillaryExportAction.java

    r31840 r31882  
    1111import java.util.ArrayList;
    1212import java.util.List;
     13import java.util.Set;
     14import java.util.concurrent.ConcurrentSkipListSet;
    1315
    1416import javax.swing.JButton;
     
    7476        export(MapillaryLayer.getInstance().getData().getImages());
    7577      } else if (this.dialog.group.isSelected(this.dialog.sequence.getModel())) {
    76         ArrayList<MapillaryAbstractImage> images = new ArrayList<>();
     78        Set<MapillaryAbstractImage> images = new ConcurrentSkipListSet<>();
    7779        for (MapillaryAbstractImage image : MapillaryLayer.getInstance()
    7880            .getData().getMultiSelectedImages())
     
    110112   *          The set of images to be exported.
    111113   */
    112   public void export(List<MapillaryAbstractImage> images) {
     114  public void export(Set<MapillaryAbstractImage> images) {
    113115    Main.worker.submit(new Thread(new MapillaryExportManager(images,
    114116        this.dialog.chooser.getSelectedFile().toString())));
  • applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/actions/MapillaryImportAction.java

    r31843 r31882  
    66import java.awt.event.ActionEvent;
    77import java.awt.event.KeyEvent;
     8import java.beans.beancontext.BeanContextChildComponentProxy;
    89import java.io.File;
    910import java.io.IOException;
    1011import java.util.ArrayList;
    1112import java.util.List;
     13import java.util.Set;
     14import java.util.concurrent.ConcurrentSkipListSet;
    1215
    1316import javax.swing.JFileChooser;
     
    6770    chooser.setMultiSelectionEnabled(true);
    6871    if (chooser.showOpenDialog(Main.parent) == JFileChooser.APPROVE_OPTION) {
    69       List<MapillaryAbstractImage> images = new ArrayList<>();
     72      Set<MapillaryAbstractImage> images = new ConcurrentSkipListSet<>();
    7073      for (File file : chooser.getSelectedFiles()) {
    7174        Main.pref.put("mapillary.start-directory", file.getParent());
  • applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/actions/MapillaryImportIntoSequenceAction.java

    r31837 r31882  
    88import java.io.File;
    99import java.io.IOException;
    10 import java.util.Collections;
    11 import java.util.Comparator;
    12 import java.util.LinkedList;
    13 import java.util.List;
     10import java.util.*;
     11import java.util.concurrent.ConcurrentSkipListSet;
    1412
    1513import javax.swing.JFileChooser;
     
    5553  @Override
    5654  public void actionPerformed(ActionEvent arg0) {
    57     this.images = new LinkedList<>();
     55    this.images = new ArrayList<>();
    5856
    5957    JFileChooser chooser = new JFileChooser();
     
    9997      }
    10098      joinImages();
    101       MapillaryRecord.getInstance().addCommand(new CommandImport(this.images));
     99      MapillaryRecord.getInstance().addCommand(new CommandImport(new ConcurrentSkipListSet(images)));
    102100    }
    103101    MapillaryUtils.showAllPictures();
  • applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/gui/MapillaryMainDialog.java

    r31843 r31882  
    3939import org.openstreetmap.josm.plugins.mapillary.actions.WalkListener;
    4040import org.openstreetmap.josm.plugins.mapillary.actions.WalkThread;
     41import org.openstreetmap.josm.plugins.mapillary.cache.CacheUtils;
    4142import org.openstreetmap.josm.plugins.mapillary.cache.MapillaryCache;
    4243import org.openstreetmap.josm.tools.ImageProvider;
     
    4748 *
    4849 * @author nokutu
    49  *
    5050 */
    5151public class MapillaryMainDialog extends ToggleDialog implements
    52     ICachedLoaderListener, MapillaryDataListener {
     52        ICachedLoaderListener, MapillaryDataListener {
    5353
    5454  private static final long serialVersionUID = 6856496736429480600L;
     
    6262  private final SideButton nextButton = new SideButton(new NextPictureAction());
    6363  private final SideButton previousButton = new SideButton(
    64       new PreviousPictureAction());
    65   /** Button used to jump to the image following the red line */
     64          new PreviousPictureAction());
     65  /**
     66   * Button used to jump to the image following the red line
     67   */
    6668  public final SideButton redButton = new SideButton(new RedAction());
    67   /** Button used to jump to the image following the blue line */
     69  /**
     70   * Button used to jump to the image following the blue line
     71   */
    6872  public final SideButton blueButton = new SideButton(new BlueAction());
    6973
     
    7680   *
    7781   * @author nokutu
    78    *
    7982   */
    8083  public enum MODE {
    81     /** Standard mode to view pictures. */
     84    /**
     85     * Standard mode to view pictures.
     86     */
    8287    NORMAL,
    83     /** Mode when in walk. */
     88    /**
     89     * Mode when in walk.
     90     */
    8491    WALK;
    8592  }
     
    8794  private JPanel buttonsPanel;
    8895
    89   /** Object containing the shown image and that handles zoom and drag */
     96  /**
     97   * Object containing the shown image and that handles zoom and drag
     98   */
    9099  public MapillaryImageDisplay mapillaryImageDisplay;
    91100
     
    95104  private MapillaryMainDialog() {
    96105    super(tr(BASE_TITLE), "mapillary.png", tr("Open Mapillary window"),
    97         Shortcut.registerShortcut(tr("Mapillary dialog"),
    98             tr("Open Mapillary main dialog"), KeyEvent.VK_M, Shortcut.NONE),
    99         200, false, MapillaryPreferenceSetting.class);
     106            Shortcut.registerShortcut(tr("Mapillary dialog"),
     107                    tr("Open Mapillary main dialog"), KeyEvent.VK_M, Shortcut.NONE),
     108            200, false, MapillaryPreferenceSetting.class);
    100109    addShortcuts();
    101110    this.mapillaryImageDisplay = new MapillaryImageDisplay();
     
    105114
    106115    createLayout(
    107         this.mapillaryImageDisplay,
    108         Arrays.asList(new SideButton[] {this.blueButton, this.previousButton, this.nextButton, this.redButton}),
    109         Main.pref.getBoolean("mapillary.reverse-buttons"));
     116            this.mapillaryImageDisplay,
     117            Arrays.asList(new SideButton[]{this.blueButton, this.previousButton, this.nextButton, this.redButton}),
     118            Main.pref.getBoolean("mapillary.reverse-buttons"));
    110119    disableAllButtons();
    111120
     
    117126  private void addShortcuts() {
    118127    this.nextButton.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
    119         KeyStroke.getKeyStroke("PAGE_DOWN"), "next");
     128            KeyStroke.getKeyStroke("PAGE_DOWN"), "next");
    120129    this.nextButton.getActionMap().put("next", new NextPictureAction());
    121130    this.previousButton.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
    122         KeyStroke.getKeyStroke("PAGE_UP"), "previous");
     131            KeyStroke.getKeyStroke("PAGE_UP"), "previous");
    123132    this.previousButton.getActionMap().put("previous",
    124         new PreviousPictureAction());
     133            new PreviousPictureAction());
    125134    this.blueButton.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
    126         KeyStroke.getKeyStroke("control PAGE_UP"), "blue");
     135            KeyStroke.getKeyStroke("control PAGE_UP"), "blue");
    127136    this.blueButton.getActionMap().put("blue", new BlueAction());
    128137    this.redButton.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
    129         KeyStroke.getKeyStroke("control PAGE_DOWN"), "red");
     138            KeyStroke.getKeyStroke("control PAGE_DOWN"), "red");
    130139    this.redButton.getActionMap().put("red", new RedAction());
    131140  }
     
    145154   * Sets a new mode for the dialog.
    146155   *
    147    * @param mode
    148    *          The mode to be set.
     156   * @param mode The mode to be set.
    149157   */
    150158  public void setMode(MODE mode) {
     
    152160      case WALK:
    153161        createLayout(
    154             this.mapillaryImageDisplay,
    155             Arrays.asList(new SideButton[] {playButton, pauseButton, stopButton}),
    156             Main.pref.getBoolean("mapillary.reverse-buttons"));
     162                this.mapillaryImageDisplay,
     163                Arrays.asList(new SideButton[]{playButton, pauseButton, stopButton}),
     164                Main.pref.getBoolean("mapillary.reverse-buttons"));
    157165        break;
    158166      case NORMAL:
    159167      default:
    160168        createLayout(
    161             this.mapillaryImageDisplay,
    162             Arrays.asList(new SideButton[] {blueButton, previousButton, nextButton, redButton}),
    163             Main.pref.getBoolean("mapillary.reverse-buttons"));
     169                this.mapillaryImageDisplay,
     170                Arrays.asList(new SideButton[]{blueButton, previousButton, nextButton, redButton}),
     171                Main.pref.getBoolean("mapillary.reverse-buttons"));
    164172        break;
    165173    }
     
    189197   * MapillaryImageDisplay object.
    190198   *
    191    * @param fullQuality
    192    *          If the full quality picture must be downloaded or just the
    193    *          thumbnail.
     199   * @param fullQuality If the full quality picture must be downloaded or just the
     200   *                    thumbnail.
    194201   */
    195202  public synchronized void updateImage(boolean fullQuality) {
     
    243250          this.thumbnailCache.cancelOutstandingTasks();
    244251        this.thumbnailCache = new MapillaryCache(mapillaryImage.getKey(),
    245             MapillaryCache.Type.THUMBNAIL);
     252                MapillaryCache.Type.THUMBNAIL);
    246253        try {
    247254          this.thumbnailCache.submit(this, false);
     
    251258
    252259        // Downloads the full resolution image.
    253         if (fullQuality) {
     260        if (fullQuality || new MapillaryCache(mapillaryImage.getKey(),
     261                MapillaryCache.Type.FULL_IMAGE).get() != null) {
    254262          if (this.imageCache != null)
    255263            this.imageCache.cancelOutstandingTasks();
    256264          this.imageCache = new MapillaryCache(mapillaryImage.getKey(),
    257               MapillaryCache.Type.FULL_IMAGE);
     265                  MapillaryCache.Type.FULL_IMAGE);
    258266          try {
    259267            this.imageCache.submit(this, false);
     
    277285  }
    278286
    279   /** Disables all the buttons in the dialog */
     287  /**
     288   * Disables all the buttons in the dialog
     289   */
    280290  private void disableAllButtons() {
    281291    this.nextButton.setEnabled(false);
     
    289299   * Sets a new MapillaryImage to be shown.
    290300   *
    291    * @param image
    292    *          The image to be shown.
     301   * @param image The image to be shown.
    293302   */
    294303  public synchronized void setImage(MapillaryAbstractImage image) {
     
    338347   *
    339348   * @author nokutu
    340    *
    341349   */
    342350  private static class NextPictureAction extends AbstractAction {
     
    362370   *
    363371   * @author nokutu
    364    *
    365372   */
    366373  private class PreviousPictureAction extends AbstractAction {
     
    374381      putValue(NAME, tr("Previous picture"));
    375382      putValue(SHORT_DESCRIPTION,
    376           tr("Shows the previous picture in the sequence"));
     383              tr("Shows the previous picture in the sequence"));
    377384    }
    378385
     
    387394   *
    388395   * @author nokutu
    389    *
    390396   */
    391397  private class RedAction extends AbstractAction {
     
    399405      putValue(NAME, tr("Jump to red"));
    400406      putValue(SHORT_DESCRIPTION,
    401           tr("Jumps to the picture at the other side of the red line"));
     407              tr("Jumps to the picture at the other side of the red line"));
    402408    }
    403409
     
    406412      if (MapillaryMainDialog.getInstance().getImage() != null) {
    407413        MapillaryLayer.getInstance().getData()
    408             .setSelectedImage(MapillaryLayer.getInstance().getRed(), true);
     414                .setSelectedImage(MapillaryLayer.getInstance().getRed(), true);
    409415      }
    410416    }
     
    415421   *
    416422   * @author nokutu
    417    *
    418423   */
    419424  private class BlueAction extends AbstractAction {
     
    427432      putValue(NAME, tr("Jump to blue"));
    428433      putValue(SHORT_DESCRIPTION,
    429           tr("Jumps to the picture at the other side of the blue line"));
     434              tr("Jumps to the picture at the other side of the blue line"));
    430435    }
    431436
     
    434439      if (MapillaryMainDialog.getInstance().getImage() != null) {
    435440        MapillaryLayer.getInstance().getData()
    436             .setSelectedImage(MapillaryLayer.getInstance().getBlue(), true);
     441                .setSelectedImage(MapillaryLayer.getInstance().getBlue(), true);
    437442      }
    438443    }
     
    528533  @Override
    529534  public void loadingFinished(final CacheEntry data,
    530       final CacheEntryAttributes attributes, final LoadResult result) {
     535                              final CacheEntryAttributes attributes, final LoadResult result) {
    531536    if (!SwingUtilities.isEventDispatchThread()) {
    532537      SwingUtilities.invokeLater(new Runnable() {
     
    543548        }
    544549        if (
    545             this.mapillaryImageDisplay.getImage() == null
    546             || img.getHeight() > this.mapillaryImageDisplay.getImage().getHeight()
    547         ) {
     550                this.mapillaryImageDisplay.getImage() == null
     551                        || img.getHeight() > this.mapillaryImageDisplay.getImage().getHeight()
     552                ) {
    548553          this.mapillaryImageDisplay.setImage(img);
    549554        }
     
    557562   * Creates the layout of the dialog.
    558563   *
    559    * @param data
    560    *          The content of the dialog
    561    * @param buttons
    562    *          The buttons where you can click
    563    * @param reverse
    564    *          {@code true} if the buttons should go at the top; {@code false}
    565    *          otherwise.
     564   * @param data    The content of the dialog
     565   * @param buttons The buttons where you can click
     566   * @param reverse {@code true} if the buttons should go at the top; {@code false}
     567   *                otherwise.
    566568   */
    567569  public void createLayout(Component data, List<SideButton> buttons,
    568       boolean reverse) {
     570                           boolean reverse) {
    569571    this.removeAll();
    570572    JPanel panel = new JPanel();
     
    575577      if (!buttons.isEmpty() && buttons.get(0) != null) {
    576578        final JPanel buttonRowPanel = new JPanel(Main.pref.getBoolean(
    577             "dialog.align.left", false) ? new FlowLayout(FlowLayout.LEFT)
    578             : new GridLayout(1, buttons.size()));
     579                "dialog.align.left", false) ? new FlowLayout(FlowLayout.LEFT)
     580                : new GridLayout(1, buttons.size()));
    579581        this.buttonsPanel.add(buttonRowPanel);
    580582        for (SideButton button : buttons)
  • applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/history/commands/CommandDelete.java

    r31840 r31882  
    77import java.util.List;
    88import java.util.Map;
     9import java.util.Set;
    910
    1011import org.openstreetmap.josm.plugins.mapillary.MapillaryAbstractImage;
     
    2728   *          The set of images that are going to be deleted.
    2829   */
    29   public CommandDelete(List<MapillaryAbstractImage> images) {
     30  public CommandDelete(Set<MapillaryAbstractImage> images) {
    3031    super(images);
    3132  }
     
    5253  @Override
    5354  public void undo() {
    54     for (int i = this.images.size() - 1; i >= 0; i--) {
    55       MapillaryAbstractImage img = this.images.get(i);
     55    for (MapillaryAbstractImage img : images) {
    5656      MapillaryLayer.getInstance().getData().add(img);
    5757      img.getSequence().getImages().add(this.changesHash.get(img), img);
  • applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/history/commands/CommandImport.java

    r31788 r31882  
    55
    66import java.util.List;
     7import java.util.Set;
    78
    89import org.openstreetmap.josm.Main;
     
    2627   *          sequence or not.
    2728   */
    28   public CommandImport(List<MapillaryAbstractImage> images) {
     29  public CommandImport(Set<MapillaryAbstractImage> images) {
    2930    super(images);
    3031  }
  • applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/history/commands/CommandJoin.java

    r31788 r31882  
    55
    66import java.util.List;
     7import java.util.concurrent.ConcurrentSkipListSet;
    78
    89import org.openstreetmap.josm.plugins.mapillary.MapillaryAbstractImage;
     
    1718public class CommandJoin extends MapillaryExecutableCommand {
    1819
     20  private MapillaryAbstractImage a;
     21  private MapillaryAbstractImage b;
     22
    1923  /**
    2024   * Main constructor.
     
    2731   */
    2832  public CommandJoin(List<MapillaryAbstractImage> images) {
    29     super(images);
     33    super(new ConcurrentSkipListSet(images));
     34    a = images.get(0);
     35    b = images.get(1);
    3036    if (images.size() != 2)
    3137      throw new IllegalArgumentException();
     
    3945  @Override
    4046  public void undo() {
    41     MapillaryUtils.unjoin(this.images.get(0), this.images.get(1));
     47    MapillaryUtils.unjoin(a, b);
    4248  }
    4349
    4450  @Override
    4551  public void redo() {
    46     MapillaryUtils.join(this.images.get(0), this.images.get(1));
     52    MapillaryUtils.join(a, b);
    4753  }
    4854
  • applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/history/commands/CommandMove.java

    r31788 r31882  
    55
    66import java.util.List;
     7import java.util.Set;
    78
    89import org.openstreetmap.josm.Main;
     
    2930   *          How much the y coordinate increases.
    3031   */
    31   public CommandMove(List<MapillaryAbstractImage> images, double x,
    32       double y) {
     32  public CommandMove(Set<MapillaryAbstractImage> images, double x,
     33                     double y) {
    3334    super(images);
    3435    this.x = x;
  • applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/history/commands/CommandTurn.java

    r31788 r31882  
    55
    66import java.util.List;
     7import java.util.Set;
    78
    89import org.openstreetmap.josm.Main;
     
    2627   *          How much the images turn.
    2728   */
    28   public CommandTurn(List<MapillaryAbstractImage> images, double ca) {
     29  public CommandTurn(Set<MapillaryAbstractImage> images, double ca) {
    2930    super(images);
    3031    this.ca = ca;
  • applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/history/commands/CommandUnjoin.java

    r31788 r31882  
    55
    66import java.util.List;
     7import java.util.Set;
     8import java.util.concurrent.ConcurrentSkipListSet;
    79
    810import org.openstreetmap.josm.plugins.mapillary.MapillaryAbstractImage;
     
    1719public class CommandUnjoin extends MapillaryExecutableCommand {
    1820
     21  private MapillaryAbstractImage a;
     22  private MapillaryAbstractImage b;
     23
    1924  /**
    2025   * Main constructor.
     
    2732   */
    2833  public CommandUnjoin(List<MapillaryAbstractImage> images) {
    29     super(images);
     34    super(new ConcurrentSkipListSet(images));
     35    a = images.get(0);
     36    b = images.get(1);
    3037    if (images.size() != 2)
    3138      throw new IllegalArgumentException();
     
    3946  @Override
    4047  public void undo() {
    41     MapillaryUtils.join(this.images.get(0), this.images.get(1));
     48    MapillaryUtils.join(a, b);
    4249  }
    4350
    4451  @Override
    4552  public void redo() {
    46     MapillaryUtils.unjoin(this.images.get(0), this.images.get(1));
     53    MapillaryUtils.unjoin(a, b);
    4754  }
    4855
  • applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/history/commands/MapillaryCommand.java

    r31840 r31882  
    44import java.util.ArrayList;
    55import java.util.List;
     6import java.util.Set;
     7import java.util.concurrent.ConcurrentSkipListSet;
    68
    79import org.openstreetmap.josm.plugins.mapillary.MapillaryAbstractImage;
     
    1517public abstract class MapillaryCommand {
    1618  /** Set of {@link MapillaryAbstractImage} objects affected by the command */
    17   public List<MapillaryAbstractImage> images;
     19  public Set<MapillaryAbstractImage> images;
    1820
    1921  /**
     
    2325   *          The images that are affected by the command.
    2426   */
    25   public MapillaryCommand(List<MapillaryAbstractImage> images) {
    26     this.images = new ArrayList<>(images);
     27  public MapillaryCommand(Set<MapillaryAbstractImage> images) {
     28    this.images = new ConcurrentSkipListSet<>(images);
    2729  }
    2830
  • applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/history/commands/MapillaryExecutableCommand.java

    r31788 r31882  
    33
    44import java.util.List;
     5import java.util.Set;
    56
    67import org.openstreetmap.josm.plugins.mapillary.MapillaryAbstractImage;
     
    2021   *          The set of images affected by the command.
    2122   */
    22   public MapillaryExecutableCommand(List<MapillaryAbstractImage> images) {
     23  public MapillaryExecutableCommand(Set<MapillaryAbstractImage> images) {
    2324    super(images);
    2425  }
  • applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/io/download/MapillarySequenceDownloadThread.java

    r31843 r31882  
    77import java.util.ArrayList;
    88import java.util.List;
     9import java.util.concurrent.ConcurrentSkipListSet;
    910import java.util.concurrent.ExecutorService;
    1011
     
    3839   * Main constructor.
    3940   *
    40    * @param ex {@link ExecutorService} executing this thread.
     41   * @param ex     {@link ExecutorService} executing this thread.
    4142   * @param bounds The bounds inside which the sequences should be downloaded
    42    * @param page the pagenumber of the results that should be retrieved
     43   * @param page   the pagenumber of the results that should be retrieved
    4344   */
    4445  public MapillarySequenceDownloadThread(ExecutorService ex, Bounds bounds, int page) {
     
    5152  public void run() {
    5253    try (
    53       BufferedReader br = new BufferedReader(new InputStreamReader(
    54           MapillaryURL.searchSequenceURL(bounds, page).openStream(),
    55           "UTF-8"
    56       ));
     54            BufferedReader br = new BufferedReader(new InputStreamReader(
     55                    MapillaryURL.searchSequenceURL(bounds, page).openStream(),
     56                    "UTF-8"
     57            ));
    5758    ) {
    5859      JsonObject jsonall = Json.createReader(br).readObject();
     
    7172          try {
    7273            images.add(new MapillaryImage(keys.getString(j), coords
    73                 .getJsonArray(j).getJsonNumber(1).doubleValue(), coords
    74                 .getJsonArray(j).getJsonNumber(0).doubleValue(), cas
    75                 .getJsonNumber(j).doubleValue()));
     74                    .getJsonArray(j).getJsonNumber(1).doubleValue(), coords
     75                    .getJsonArray(j).getJsonNumber(0).doubleValue(), cas
     76                    .getJsonNumber(j).doubleValue()));
    7677          } catch (IndexOutOfBoundsException e) {
    7778            Main.warn("Mapillary bug at " + MapillaryURL.searchSequenceURL(bounds, page));
     
    8283          break;
    8384        MapillarySequence sequence = new MapillarySequence(
    84             jsonobj.getString("key"), jsonobj.getJsonNumber("captured_at")
     85                jsonobj.getString("key"), jsonobj.getJsonNumber("captured_at")
    8586                .longValue());
    8687        List<MapillaryImage> finalImages = new ArrayList<>(images);
     
    9798                // The image in finalImages is substituted by the one in the
    9899                // database, as they represent the same picture.
    99                 img = (MapillaryImage) MapillaryLayer.getInstance().getData().getImages()
    100                     .get(MapillaryLayer.getInstance().getData().getImages().indexOf(img));
     100                for (MapillaryAbstractImage source : MapillaryLayer.getInstance().getData().getImages()) {
     101                  if (source.equals(img)) {
     102                    img = (MapillaryImage) source;
     103                  }
     104                }
    101105                sequence.add(img);
    102                 ((MapillaryImage) MapillaryLayer.getInstance().getData().getImages()
    103                     .get(MapillaryLayer.getInstance().getData().getImages().indexOf(img)))
    104                     .setSequence(sequence);
     106                img.setSequence(sequence);
    105107                finalImages.set(finalImages.indexOf(img), img);
    106108              } else {
     
    112114        }
    113115
    114         MapillaryLayer.getInstance().getData().add(new ArrayList<MapillaryAbstractImage>(finalImages), false);
     116        MapillaryLayer.getInstance().getData().add(new ConcurrentSkipListSet(finalImages), false);
    115117      }
    116118    } catch (IOException e) {
  • applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/io/export/MapillaryExportManager.java

    r31811 r31882  
    77import java.io.IOException;
    88import java.util.List;
     9import java.util.Set;
    910import java.util.concurrent.ArrayBlockingQueue;
    1011import java.util.concurrent.ThreadPoolExecutor;
     
    3839
    3940  private final int amount;
    40   private List<MapillaryAbstractImage> images;
     41  private Set<MapillaryAbstractImage> images;
    4142  private String path;
    4243
     
    5253   *          Export path.
    5354   */
    54   public MapillaryExportManager(List<MapillaryAbstractImage> images, String path) {
     55  public MapillaryExportManager(Set<MapillaryAbstractImage> images, String path) {
    5556    super(tr("Downloading") + "...", new PleaseWaitProgressMonitor(
    5657        "Exporting Mapillary Images"), true);
  • applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/mode/SelectMode.java

    r31811 r31882  
    99import java.awt.event.MouseEvent;
    1010import java.util.ArrayList;
     11import java.util.concurrent.ConcurrentSkipListSet;
    1112
    1213import org.openstreetmap.josm.Main;
     
    2930 *
    3031 * @author nokutu
    31  *
    3232 */
    3333public class SelectMode extends AbstractMode {
     
    5454    MapillaryAbstractImage closest = getClosest(e.getPoint());
    5555    if (!(Main.map.mapView.getActiveLayer() instanceof MapillaryLayer)
    56         && closest != null && Main.map.mapMode == Main.map.mapModeSelect) {
     56            && closest != null && Main.map.mapMode == Main.map.mapModeSelect) {
    5757      this.lastClicked = this.closest;
    5858      this.data.setSelectedImage(closest);
    5959      return;
    6060    } else if (Main.map.mapView.getActiveLayer() != MapillaryLayer
    61         .getInstance())
     61            .getInstance())
    6262      return;
    6363    // Double click
    6464    if (e.getClickCount() == 2 && this.data.getSelectedImage() != null
    65         && closest != null) {
     65            && closest != null) {
    6666      for (MapillaryAbstractImage img : closest.getSequence().getImages()) {
    6767        this.data.addMultiSelectedImage(img);
     
    7171    this.lastClicked = this.closest;
    7272    this.closest = closest;
    73     if (this.data.getMultiSelectedImages().contains(closest))
     73    if (closest != null && this.data.getMultiSelectedImages().contains(closest))
    7474      return;
    7575    // ctrl+click
    7676    if (e.getModifiers() == (InputEvent.BUTTON1_MASK | InputEvent.CTRL_MASK)
    77         && closest != null)
     77            && closest != null)
    7878      this.data.addMultiSelectedImage(closest);
    79     // shift + click
     79      // shift + click
    8080    else if (e.getModifiers() == (InputEvent.BUTTON1_MASK | InputEvent.SHIFT_MASK)
    81         && this.lastClicked instanceof MapillaryImage) {
     81            && this.lastClicked instanceof MapillaryImage) {
    8282      if (this.closest != null && this.lastClicked != null
    83           && this.closest.getSequence() == (this.lastClicked).getSequence()) {
     83              && this.closest.getSequence() == (this.lastClicked).getSequence()) {
    8484        int i = this.closest.getSequence().getImages().indexOf(this.closest);
    8585        int j = this.lastClicked.getSequence().getImages()
    86             .indexOf(this.lastClicked);
     86                .indexOf(this.lastClicked);
    8787        if (i < j)
    88           this.data.addMultiSelectedImage(new ArrayList<>(this.closest.getSequence()
    89               .getImages().subList(i, j + 1)));
     88          this.data.addMultiSelectedImage(new ConcurrentSkipListSet(this.closest.getSequence()
     89                  .getImages().subList(i, j + 1)));
    9090        else
    91           this.data.addMultiSelectedImage(new ArrayList<>(this.closest.getSequence()
    92               .getImages().subList(j, i + 1)));
     91          this.data.addMultiSelectedImage(new ConcurrentSkipListSet(this.closest.getSequence()
     92                  .getImages().subList(j, i + 1)));
    9393      }
    9494      // click
     
    9999  @Override
    100100  public void mouseDragged(MouseEvent e) {
    101     if (Main.map.mapView.getActiveLayer() != MapillaryLayer.getInstance())
    102       return;
     101    if (Main.map.mapView.getActiveLayer() != MapillaryLayer.getInstance()) {
     102      return;
     103    }
    103104
    104105    if (!Main.pref.getBoolean("mapillary.developer"))
     
    118119      } else if (this.lastButton == MouseEvent.BUTTON1 && e.isShiftDown()) {
    119120        this.closest.turn(Math.toDegrees(Math.atan2((e.getX() - this.start.x),
    120             -(e.getY() - this.start.y)))
    121             - this.closest.getTempCa());
     121                -(e.getY() - this.start.y)))
     122                - this.closest.getTempCa());
    122123        for (MapillaryAbstractImage img : this.data.getMultiSelectedImages()) {
    123124          img.turn(Math.toDegrees(Math.atan2((e.getX() - this.start.x),
    124               -(e.getY() - this.start.y))) - this.closest.getTempCa());
     125                  -(e.getY() - this.start.y))) - this.closest.getTempCa());
    125126        }
    126127        Main.map.repaint();
     
    137138      double to = this.data.getSelectedImage().getCa();
    138139      this.record.addCommand(new CommandTurn(this.data.getMultiSelectedImages(), to
    139           - from));
     140              - from));
    140141    } else if (this.data.getSelectedImage().getTempLatLon() != this.data
    141         .getSelectedImage().getLatLon()) {
     142            .getSelectedImage().getLatLon()) {
    142143      LatLon from = this.data.getSelectedImage().getTempLatLon();
    143144      LatLon to = this.data.getSelectedImage().getLatLon();
    144145      this.record.addCommand(new CommandMove(this.data.getMultiSelectedImages(), to
    145           .getX() - from.getX(), to.getY() - from.getY()));
     146              .getX() - from.getX(), to.getY() - from.getY()));
    146147    }
    147148    for (MapillaryAbstractImage img : this.data.getMultiSelectedImages()) {
     
    156157  @Override
    157158  public void mouseMoved(MouseEvent e) {
     159    if (Main.map.mapView.getActiveLayer() instanceof OsmDataLayer
     160            && Main.map.mapMode != Main.map.mapModeSelect) {
     161      return;
     162    }
    158163    MapillaryAbstractImage closestTemp = getClosest(e.getPoint());
    159     if (Main.map.mapView.getActiveLayer() instanceof OsmDataLayer
    160         && Main.map.mapMode != Main.map.mapModeSelect)
    161       return;
     164
    162165    if (closestTemp != null
    163         && Main.map.mapView.getActiveLayer() instanceof OsmDataLayer
    164         && !this.imageHighlighted) {
     166            && Main.map.mapView.getActiveLayer() instanceof OsmDataLayer
     167            && !this.imageHighlighted) {
    165168      Main.map.mapMode.putValue("active", Boolean.FALSE);
    166169      this.imageHighlighted = true;
    167170
    168171    } else if (closestTemp == null
    169         && Main.map.mapView.getActiveLayer() instanceof OsmDataLayer
    170         && this.imageHighlighted && this.nothingHighlighted) {
     172            && Main.map.mapView.getActiveLayer() instanceof OsmDataLayer
     173            && this.imageHighlighted && this.nothingHighlighted) {
    171174      this.nothingHighlighted = false;
    172175      Main.map.mapMode.putValue("active", Boolean.TRUE);
    173176
    174177    } else if (this.imageHighlighted && !this.nothingHighlighted
    175         && Main.map.mapView.getEditLayer().data != null
    176         && Main.map.mapView.getActiveLayer() instanceof OsmDataLayer) {
     178            && Main.map.mapView.getEditLayer().data != null
     179            && Main.map.mapView.getActiveLayer() instanceof OsmDataLayer) {
    177180
    178181      for (OsmPrimitive primivitive : Main.map.mapView.getEditLayer().data
    179           .allPrimitives()) {
     182              .allPrimitives()) {
    180183        primivitive.setHighlighted(false);
    181184      }
  • applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/oauth/UploadUtils.java

    r31842 r31882  
    1010import java.util.HashMap;
    1111import java.util.List;
     12import java.util.Set;
    1213import java.util.UUID;
    1314import java.util.concurrent.ArrayBlockingQueue;
     15import java.util.concurrent.ConcurrentSkipListSet;
    1416import java.util.concurrent.ThreadPoolExecutor;
    1517import java.util.concurrent.TimeUnit;
     
    5254 *
    5355 * @author nokutu
    54  *
    5556 */
    5657public class UploadUtils {
    5758
    58   /** Required keys for POST */
    59   private static final String[] keys = { "key", "AWSAccessKeyId", "acl",
    60       "policy", "signature", "Content-Type" };
    61 
    62   /** Mapillary upload URL */
     59  /**
     60   * Required keys for POST
     61   */
     62  private static final String[] keys = {"key", "AWSAccessKeyId", "acl",
     63          "policy", "signature", "Content-Type"};
     64
     65  /**
     66   * Mapillary upload URL
     67   */
    6368  private static final String UPLOAD_URL = "https://s3-eu-west-1.amazonaws.com/mapillary.uploads.manual.images";
    6469
    65   /** Count to name temporal files. */
     70  /**
     71   * Count to name temporal files.
     72   */
    6673  private static int c;
    6774
    6875  private static class SequenceUploadThread extends Thread {
    69     private final List<MapillaryAbstractImage> images;
     76    private final Set<MapillaryAbstractImage> images;
    7077    private final UUID uuid;
    7178    private final boolean delete;
    7279    private final ThreadPoolExecutor ex;
    7380
    74     private SequenceUploadThread(List<MapillaryAbstractImage> images,
    75         boolean delete) {
     81    private SequenceUploadThread(Set<MapillaryAbstractImage> images,
     82                                 boolean delete) {
    7683      this.images = images;
    7784      this.uuid = UUID.randomUUID();
     
    104111      if (this.delete)
    105112        MapillaryRecord.getInstance()
    106             .addCommand(new CommandDelete(this.images));
    107     }
    108   }
     113                .addCommand(new CommandDelete(images));
     114    }
     115  }
     116
    109117  private static class SingleUploadThread extends Thread {
    110118
     
    129137   * @param image
    130138   * @return A File object containing the picture and an updated version of the
    131    *         EXIF tags.
    132    * @throws ImageReadException
    133    *           if there are errors reading the image from the file.
    134    * @throws IOException
    135    *           if there are errors getting the metadata from the file or writing
    136    *           the output.
    137    * @throws ImageWriteException
    138    *           if there are errors writing the image in the file.
     139   * EXIF tags.
     140   * @throws ImageReadException  if there are errors reading the image from the file.
     141   * @throws IOException         if there are errors getting the metadata from the file or writing
     142   *                             the output.
     143   * @throws ImageWriteException if there are errors writing the image in the file.
    139144   */
    140145  public static File updateFile(MapillaryImportedImage image)
    141       throws ImageReadException, IOException, ImageWriteException {
     146          throws ImageReadException, IOException, ImageWriteException {
    142147    TiffOutputSet outputSet = null;
    143148    TiffOutputDirectory exifDirectory = null;
     
    167172    gpsDirectory.removeField(GpsTagConstants.GPS_TAG_GPS_IMG_DIRECTION_REF);
    168173    gpsDirectory.add(GpsTagConstants.GPS_TAG_GPS_IMG_DIRECTION_REF,
    169         GpsTagConstants.GPS_TAG_GPS_IMG_DIRECTION_REF_VALUE_TRUE_NORTH);
     174            GpsTagConstants.GPS_TAG_GPS_IMG_DIRECTION_REF_VALUE_TRUE_NORTH);
    170175
    171176    gpsDirectory.removeField(GpsTagConstants.GPS_TAG_GPS_IMG_DIRECTION);
    172177    gpsDirectory.add(GpsTagConstants.GPS_TAG_GPS_IMG_DIRECTION,
    173         RationalNumber.valueOf(image.getCa()));
     178            RationalNumber.valueOf(image.getCa()));
    174179
    175180    exifDirectory.removeField(ExifTagConstants.EXIF_TAG_DATE_TIME_ORIGINAL);
    176181    exifDirectory.add(ExifTagConstants.EXIF_TAG_DATE_TIME_ORIGINAL,
    177         ((MapillaryImportedImage) image).getDate("yyyy/MM/dd HH:mm:ss"));
     182            ((MapillaryImportedImage) image).getDate("yyyy/MM/dd HH:mm:ss"));
    178183
    179184    // Removes the ImageDescription tag, that causes problems in the upload.
     
    197202   *
    198203   * @param image
    199    *
    200204   */
    201205  public static void upload(MapillaryImportedImage image) {
     
    205209  /**
    206210   * @param image
    207    * @param uuid
    208    *          The UUID used to create the sequence.
     211   * @param uuid  The UUID used to create the sequence.
    209212   */
    210213  public static void upload(MapillaryImportedImage image, UUID uuid) {
    211214    String key = MapillaryUser.getUsername() + "/" + uuid.toString() + "/"
    212         + image.getLatLon().lat() + "_" + image.getLatLon().lon() + "_"
    213         + image.getCa() + "_" + image.getCapturedAt() + ".jpg";
     215            + image.getLatLon().lat() + "_" + image.getLatLon().lon() + "_"
     216            + image.getCa() + "_" + image.getCapturedAt() + ".jpg";
    214217
    215218    String policy = null;
     
    236239   * @param hash
    237240   * @throws IOException
    238    * @throws IllegalArgumentException
    239    *           if the hash doesn't contain all the needed keys.
     241   * @throws IllegalArgumentException if the hash doesn't contain all the needed keys.
    240242   */
    241243  public static void uploadFile(File file, HashMap<String, String> hash)
    242       throws IOException {
     244          throws IOException {
    243245    HttpClientBuilder builder = HttpClientBuilder.create();
    244246    HttpClient httpClient = builder.build();
     
    250252        throw new IllegalArgumentException();
    251253      entityBuilder.addPart(key, new StringBody(hash.get(key),
    252           ContentType.TEXT_PLAIN));
     254              ContentType.TEXT_PLAIN));
    253255    }
    254256    entityBuilder.addPart("file", new FileBody(file));
     
    268270   * Uploads the given {@link MapillarySequence}.
    269271   *
    270    * @param sequence
    271    *          The sequence to upload. It must contain only
    272    *          {@link MapillaryImportedImage} objects.
    273    * @param delete
    274    *          Whether the images must be deleted after upload or not.
     272   * @param sequence The sequence to upload. It must contain only
     273   *                 {@link MapillaryImportedImage} objects.
     274   * @param delete   Whether the images must be deleted after upload or not.
    275275   */
    276276  public static void uploadSequence(MapillarySequence sequence, boolean delete) {
    277     Main.worker.submit(new SequenceUploadThread(sequence.getImages(), delete));
     277    Main.worker.submit(new SequenceUploadThread(new ConcurrentSkipListSet(sequence.getImages()), delete));
    278278  }
    279279}
  • applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/utils/MapillaryUtils.java

    r31842 r31882  
    1111import java.text.ParseException;
    1212import java.text.SimpleDateFormat;
    13 import java.util.ArrayList;
    14 import java.util.Calendar;
    15 import java.util.Date;
    16 import java.util.List;
    17 import java.util.Locale;
     13import java.util.*;
    1814
    1915import javax.swing.SwingUtilities;
     
    4036 *
    4137 * @author nokutu
    42  *
    4338 */
    4439public final class MapillaryUtils {
     
    9792   * ({@link java.util.TimeZone#getDefault()}).
    9893   *
    99    * @param date The string containing the date.
     94   * @param date   The string containing the date.
    10095   * @param format The format of the date.
    10196   * @return The date in Epoch format.
     
    110105   * degrees-minutes-seconds-format
    111106   *
    112    * @param degMinSec
    113    *          an array of length 3, the values in there are (in this order)
    114    *          degrees, minutes and seconds
    115    * @param ref
    116    *          the latitude or longitude reference determining if the given value
    117    *          is:
    118    *          <ul>
    119    *          <li>north (
    120    *          {@link GpsTagConstants#GPS_TAG_GPS_LATITUDE_REF_VALUE_NORTH}) or
    121    *          south (
    122    *          {@link GpsTagConstants#GPS_TAG_GPS_LATITUDE_REF_VALUE_SOUTH}) of
    123    *          the equator</li>
    124    *          <li>east (
    125    *          {@link GpsTagConstants#GPS_TAG_GPS_LONGITUDE_REF_VALUE_EAST}) or
    126    *          west ({@link GpsTagConstants#GPS_TAG_GPS_LONGITUDE_REF_VALUE_WEST}
    127    *          ) of the equator</li>
    128    *          </ul>
     107   * @param degMinSec an array of length 3, the values in there are (in this order)
     108   *                  degrees, minutes and seconds
     109   * @param ref       the latitude or longitude reference determining if the given value
     110   *                  is:
     111   *                  <ul>
     112   *                  <li>north (
     113   *                  {@link GpsTagConstants#GPS_TAG_GPS_LATITUDE_REF_VALUE_NORTH}) or
     114   *                  south (
     115   *                  {@link GpsTagConstants#GPS_TAG_GPS_LATITUDE_REF_VALUE_SOUTH}) of
     116   *                  the equator</li>
     117   *                  <li>east (
     118   *                  {@link GpsTagConstants#GPS_TAG_GPS_LONGITUDE_REF_VALUE_EAST}) or
     119   *                  west ({@link GpsTagConstants#GPS_TAG_GPS_LONGITUDE_REF_VALUE_WEST}
     120   *                  ) of the equator</li>
     121   *                  </ul>
    129122   * @return the decimal degree-value for the given input, negative when west of
    130    *         0-meridian or south of equator, positive otherwise
    131    * @throws IllegalArgumentException
    132    *           if {@code degMinSec} doesn't have length 3 or if {@code ref} is
    133    *           not one of the values mentioned above
     123   * 0-meridian or south of equator, positive otherwise
     124   * @throws IllegalArgumentException if {@code degMinSec} doesn't have length 3 or if {@code ref} is
     125   *                                  not one of the values mentioned above
    134126   */
    135127  public static double degMinSecToDouble(RationalNumber[] degMinSec,
    136       String ref) {
     128                                         String ref) {
    137129    if (degMinSec == null || degMinSec.length != 3) {
    138130      throw new IllegalArgumentException("Array's length must be 3.");
     
    157149
    158150    if (GpsTagConstants.GPS_TAG_GPS_LATITUDE_REF_VALUE_SOUTH.equals(ref)
    159         || GpsTagConstants.GPS_TAG_GPS_LONGITUDE_REF_VALUE_WEST.equals(ref)) {
     151            || GpsTagConstants.GPS_TAG_GPS_LONGITUDE_REF_VALUE_WEST.equals(ref)) {
    160152      result *= -1;
    161153    }
     
    168160   * Returns the extension of a {@link File} object.
    169161   *
    170    * @param file
    171    *          The {@link File} object whose extension is going to be returned.
     162   * @param file The {@link File} object whose extension is going to be returned.
    172163   * @return A {@code String} object containing the extension in lowercase.
    173164   */
     
    189180   */
    190181  public static synchronized void join(
    191       MapillaryAbstractImage mapillaryAbstractImage,
    192       MapillaryAbstractImage mapillaryAbstractImage2) {
     182          MapillaryAbstractImage mapillaryAbstractImage,
     183          MapillaryAbstractImage mapillaryAbstractImage2) {
    193184    MapillaryAbstractImage firstImage = mapillaryAbstractImage;
    194185    MapillaryAbstractImage secondImage = mapillaryAbstractImage2;
     
    221212   * direction) and creates a new icon in that position.
    222213   *
    223    * @param file
    224    *          The file where the picture is located.
     214   * @param file The file where the picture is located.
    225215   * @return The imported image.
    226    * @throws ImageReadException
    227    *           If the file isn't an image.
    228    * @throws IOException
    229    *           If the file doesn't have the valid metadata.
     216   * @throws ImageReadException If the file isn't an image.
     217   * @throws IOException        If the file doesn't have the valid metadata.
    230218   */
    231219  public static MapillaryImportedImage readJPG(File file)
    232       throws IOException, ImageReadException {
     220          throws IOException, ImageReadException {
    233221    return readJPG(file, false);
    234222  }
     
    238226   * direction) and creates a new icon in that position.
    239227   *
    240    * @param file
    241    *          The {@link File} where the picture is located.
    242    * @param exceptionNoTags
    243    *          {@code true} if an exception must be thrown if the image doesn't
    244    *          have all the needed EXIF tags; {@code false} returns an image in
    245    *          the center of the screen.
     228   * @param file            The {@link File} where the picture is located.
     229   * @param exceptionNoTags {@code true} if an exception must be thrown if the image doesn't
     230   *                        have all the needed EXIF tags; {@code false} returns an image in
     231   *                        the center of the screen.
    246232   * @return The imported image, whose data has been extracted from the
    247    *         picture's metadata.
    248    * @throws ImageReadException
    249    *           If the {@link File} isn't an image.
    250    * @throws IOException
    251    *           If the {@link File} doesn't have the valid metadata.
    252    * @throws IllegalArgumentException
    253    *           if exceptionNoTags is set to {@code true} and the image doesn't
    254    *           have the needed EXIF tags.
     233   * picture's metadata.
     234   * @throws ImageReadException       If the {@link File} isn't an image.
     235   * @throws IOException              If the {@link File} doesn't have the valid metadata.
     236   * @throws IllegalArgumentException if exceptionNoTags is set to {@code true} and the image doesn't
     237   *                                  have the needed EXIF tags.
    255238   */
    256239  public static MapillaryImportedImage readJPG(File file,
    257       boolean exceptionNoTags) throws IOException, ImageReadException {
     240                                               boolean exceptionNoTags) throws IOException, ImageReadException {
    258241    final ImageMetadata metadata = Imaging.getMetadata(file);
    259242    if (metadata instanceof JpegImageMetadata) {
    260243      final JpegImageMetadata jpegMetadata = (JpegImageMetadata) metadata;
    261244      final TiffField lat_ref = jpegMetadata.findEXIFValueWithExactMatch(
    262           GpsTagConstants.GPS_TAG_GPS_LATITUDE_REF);
     245              GpsTagConstants.GPS_TAG_GPS_LATITUDE_REF);
    263246      final TiffField lat = jpegMetadata
    264           .findEXIFValueWithExactMatch(GpsTagConstants.GPS_TAG_GPS_LATITUDE);
     247              .findEXIFValueWithExactMatch(GpsTagConstants.GPS_TAG_GPS_LATITUDE);
    265248      final TiffField lon_ref = jpegMetadata.findEXIFValueWithExactMatch(
    266           GpsTagConstants.GPS_TAG_GPS_LONGITUDE_REF);
     249              GpsTagConstants.GPS_TAG_GPS_LONGITUDE_REF);
    267250      final TiffField lon = jpegMetadata
    268           .findEXIFValueWithExactMatch(GpsTagConstants.GPS_TAG_GPS_LONGITUDE);
     251              .findEXIFValueWithExactMatch(GpsTagConstants.GPS_TAG_GPS_LONGITUDE);
    269252      final TiffField ca = jpegMetadata.findEXIFValueWithExactMatch(
    270           GpsTagConstants.GPS_TAG_GPS_IMG_DIRECTION);
     253              GpsTagConstants.GPS_TAG_GPS_IMG_DIRECTION);
    271254      final TiffField datetimeOriginal = jpegMetadata
    272           .findEXIFValueWithExactMatch(
    273               ExifTagConstants.EXIF_TAG_DATE_TIME_ORIGINAL);
     255              .findEXIFValueWithExactMatch(
     256                      ExifTagConstants.EXIF_TAG_DATE_TIME_ORIGINAL);
    274257      if (lat_ref == null || lat == null || lon == null || lon_ref == null) {
    275258        if (exceptionNoTags)
    276259          throw new IllegalArgumentException(
    277               "The image doesn't have the needed EXIF tags.");
     260                  "The image doesn't have the needed EXIF tags.");
    278261        else
    279262          return readNoTags(file);
     
    284267      if (lat.getValue() instanceof RationalNumber[])
    285268        latValue = MapillaryUtils.degMinSecToDouble(
    286             (RationalNumber[]) lat.getValue(), lat_ref.getValue().toString());
     269                (RationalNumber[]) lat.getValue(), lat_ref.getValue().toString());
    287270      if (lon.getValue() instanceof RationalNumber[])
    288271        lonValue = MapillaryUtils.degMinSecToDouble(
    289             (RationalNumber[]) lon.getValue(), lon_ref.getValue().toString());
     272                (RationalNumber[]) lon.getValue(), lon_ref.getValue().toString());
    290273      if (ca != null && ca.getValue() instanceof RationalNumber)
    291274        caValue = ((RationalNumber) ca.getValue()).doubleValue();
    292275      if (datetimeOriginal != null)
    293276        return new MapillaryImportedImage(latValue, lonValue, caValue, file,
    294             datetimeOriginal.getStringValue());
     277                datetimeOriginal.getStringValue());
    295278      else
    296279        return new MapillaryImportedImage(latValue, lonValue, caValue, file);
     
    303286   * creates a new icon in the middle of the map.
    304287   *
    305    * @param file
    306    *          The file where the image is located.
     288   * @param file The file where the image is located.
    307289   * @return The imported image.
    308290   */
    309291  public static MapillaryImportedImage readNoTags(File file) {
    310292    return readNoTags(file, Main.map.mapView.getProjection()
    311         .eastNorth2latlon(Main.map.mapView.getCenter()));
     293            .eastNorth2latlon(Main.map.mapView.getCenter()));
    312294  }
    313295
     
    316298   * creates a new icon in the middle of the map.
    317299   *
    318    * @param file
    319    *          The file where the image is located.
    320    * @param pos
    321    *          A {@link LatLon} object indicating the position in the map where
    322    *          the image must be set.
     300   * @param file The file where the image is located.
     301   * @param pos  A {@link LatLon} object indicating the position in the map where
     302   *             the image must be set.
    323303   * @return The imported image.
    324304   */
     
    335315      final JpegImageMetadata jpegMetadata = (JpegImageMetadata) metadata;
    336316      final TiffField datetimeOriginal = jpegMetadata
    337           .findEXIFValueWithExactMatch(
    338               ExifTagConstants.EXIF_TAG_DATE_TIME_ORIGINAL);
     317              .findEXIFValueWithExactMatch(
     318                      ExifTagConstants.EXIF_TAG_DATE_TIME_ORIGINAL);
    339319      if (datetimeOriginal == null)
    340320        return new MapillaryImportedImage(pos.lat(), pos.lon(), 0,
    341             file);
     321                file);
    342322      else {
    343323        try {
    344324          return new MapillaryImportedImage(pos.lat(), pos.lon(), 0,
    345               file, datetimeOriginal.getStringValue());
     325                  file, datetimeOriginal.getStringValue());
    346326        } catch (ImageReadException e) {
    347327          Main.error(e);
     
    355335   * Reads an image in PNG format.
    356336   *
    357    * @param file
    358    *          The file where the image is located.
     337   * @param file The file where the image is located.
    359338   * @return The imported image.
    360339   */
     
    374353   * Zooms to fit all the given {@link MapillaryAbstractImage} objects.
    375354   *
    376    * @param images
    377    *          The images your are zooming to.
    378    * @param select
    379    *          Whether the added images must be selected or not.
    380    */
    381   public static void showPictures(final List<MapillaryAbstractImage> images, final boolean select) {
     355   * @param images The images your are zooming to.
     356   * @param select Whether the added images must be selected or not.
     357   */
     358  public static void showPictures(final Set<MapillaryAbstractImage> images, final boolean select) {
    382359    if (!SwingUtilities.isEventDispatchThread()) {
    383360      SwingUtilities.invokeLater(new Runnable() {
     
    388365      });
    389366    } else {
    390       Bounds zoomBounds;
     367      Bounds zoomBounds = null;
    391368      if (images.isEmpty()) {
    392369        zoomBounds = new Bounds(new LatLon(0, 0));
    393370      } else {
    394         zoomBounds = new Bounds(images.get(0).getLatLon());
    395371        for (MapillaryAbstractImage img : images) {
    396           zoomBounds.extend(img.getLatLon());
     372          if (zoomBounds == null) {
     373            zoomBounds = new Bounds(img.getLatLon());
     374          } else
     375            zoomBounds.extend(img.getLatLon());
    397376        }
    398377      }
     378
    399379      // The zoom rectangle must have a minimum size.
    400380      double latExtent = Math.max(zoomBounds.getMaxLat() - zoomBounds.getMinLat(), MIN_ZOOM_SQUARE_SIDE);
     
    409389        MapillaryData.dataUpdated();
    410390    }
     391
    411392  }
    412393
     
    418399   */
    419400  public static synchronized void unjoin(
    420       MapillaryAbstractImage mapillaryAbstractImage,
    421       MapillaryAbstractImage mapillaryAbstractImage2) {
     401          MapillaryAbstractImage mapillaryAbstractImage,
     402          MapillaryAbstractImage mapillaryAbstractImage2) {
    422403    MapillaryAbstractImage firstImage = mapillaryAbstractImage;
    423404    MapillaryAbstractImage secondImage = mapillaryAbstractImage2;
     
    429410
    430411    ArrayList<MapillaryAbstractImage> firstHalf = new ArrayList<>(
    431         firstImage.getSequence().getImages().subList(0,
    432             firstImage.getSequence().getImages().indexOf(secondImage)));
     412            firstImage.getSequence().getImages().subList(0,
     413                    firstImage.getSequence().getImages().indexOf(secondImage)));
    433414    ArrayList<MapillaryAbstractImage> secondHalf = new ArrayList<>(
    434         firstImage.getSequence().getImages().subList(
    435             firstImage.getSequence().getImages().indexOf(secondImage),
    436             firstImage.getSequence().getImages().size()));
     415            firstImage.getSequence().getImages().subList(
     416                    firstImage.getSequence().getImages().indexOf(secondImage),
     417                    firstImage.getSequence().getImages().size()));
    437418
    438419    MapillarySequence seq1 = new MapillarySequence();
     
    457438    StringBuilder ret = new StringBuilder();
    458439    if (PluginState.isDownloading()) {
    459       ret .append(tr("Downloading Mapillary images"));
     440      ret.append(tr("Downloading Mapillary images"));
    460441    } else if (MapillaryLayer.getInstance().getData().size() > 0) {
    461442      ret.append(tr("Total Mapillary images: {0}", MapillaryLayer.getInstance().getData().size()));
Note: See TracChangeset for help on using the changeset viewer.