Index: /applications/editors/josm/plugins/mapillary/.gitignore
===================================================================
--- /applications/editors/josm/plugins/mapillary/.gitignore	(revision 31881)
+++ /applications/editors/josm/plugins/mapillary/.gitignore	(revision 31882)
@@ -6,2 +6,4 @@
 /.settings
 /test/data/preferences/
+*.iml
+.idea/
Index: /applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/MapillaryAbstractImage.java
===================================================================
--- /applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/MapillaryAbstractImage.java	(revision 31881)
+++ /applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/MapillaryAbstractImage.java	(revision 31882)
@@ -17,5 +17,5 @@
  *
  */
-public class MapillaryAbstractImage {
+public class MapillaryAbstractImage implements Comparable<MapillaryAbstractImage>{
   /**
    * If two values for field ca differ by less than EPSILON both values are considered equal.
@@ -120,5 +120,5 @@
     final Date date = new Date(getCapturedAt());
 
-    final SimpleDateFormat formatter = new SimpleDateFormat(format, Locale.UK);
+    final SimpleDateFormat formatter = new SimpleDateFormat(format);
     formatter.setTimeZone(Calendar.getInstance().getTimeZone());
     return formatter.format(date);
@@ -267,3 +267,8 @@
     this.movingCa = this.tempCa + ca;
   }
+
+  @Override
+  public int compareTo(MapillaryAbstractImage mapillaryAbstractImage) {
+    return hashCode() - mapillaryAbstractImage.hashCode();
+  }
 }
Index: /applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/MapillaryData.java
===================================================================
--- /applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/MapillaryData.java	(revision 31881)
+++ /applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/MapillaryData.java	(revision 31882)
@@ -2,6 +2,7 @@
 package org.openstreetmap.josm.plugins.mapillary;
 
-import java.util.ArrayList;
-import java.util.List;
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentSkipListSet;
 import java.util.concurrent.CopyOnWriteArrayList;
 
@@ -17,18 +18,27 @@
  * @see MapillaryAbstractImage
  * @see MapillarySequence
- *
  */
 public class MapillaryData {
 
-  private List<MapillaryAbstractImage> images;
-  /** The image currently selected, this is the one being shown. */
+  private Set<MapillaryAbstractImage> images;
+  /**
+   * The image currently selected, this is the one being shown.
+   */
   private MapillaryAbstractImage selectedImage;
-  /** The image under the cursor. */
+  /**
+   * The image under the cursor.
+   */
   private MapillaryAbstractImage highlightedImage;
-  /** All the images selected, can be more than one. */
-  private final List<MapillaryAbstractImage> multiSelectedImages;
-  /** Listeners of the class. */
+  /**
+   * All the images selected, can be more than one.
+   */
+  private final Set<MapillaryAbstractImage> multiSelectedImages;
+  /**
+   * Listeners of the class.
+   */
   private final CopyOnWriteArrayList<MapillaryDataListener> listeners = new CopyOnWriteArrayList<>();
-  /** The bounds of the areas for which the pictures have been downloaded. */
+  /**
+   * The bounds of the areas for which the pictures have been downloaded.
+   */
   public List<Bounds> bounds;
 
@@ -37,6 +47,6 @@
    */
   protected MapillaryData() {
-    this.images = new CopyOnWriteArrayList<>();
-    this.multiSelectedImages = new ArrayList<>();
+    this.images = new ConcurrentSkipListSet<>();
+    this.multiSelectedImages = new ConcurrentSkipListSet<>();
     this.selectedImage = null;
 
@@ -52,6 +62,5 @@
    * Adds an MapillaryImage to the object, and then repaints mapView.
    *
-   * @param image
-   *          The image to be added.
+   * @param image The image to be added.
    */
   public synchronized void add(MapillaryAbstractImage image) {
@@ -63,8 +72,6 @@
    * needed for concurrency.
    *
-   * @param image
-   *          The image to be added.
-   * @param update
-   *          Whether the map must be updated or not.
+   * @param image  The image to be added.
+   * @param update Whether the map must be updated or not.
    */
   public synchronized void add(MapillaryAbstractImage image, boolean update) {
@@ -80,8 +87,7 @@
    * Adds a set of MapillaryImages to the object, and then repaints mapView.
    *
-   * @param images
-   *          The set of images to be added.
-   */
-  public synchronized void add(List<MapillaryAbstractImage> images) {
+   * @param images The set of images to be added.
+   */
+  public synchronized void add(Set<MapillaryAbstractImage> images) {
     add(images, true);
   }
@@ -90,10 +96,8 @@
    * Adds a set of {link MapillaryAbstractImage} objects to this object.
    *
-   * @param images
-   *          The set of images to be added.
-   * @param update
-   *          Whether the map must be updated or not.
-   */
-  public synchronized void add(List<MapillaryAbstractImage> images, boolean update) {
+   * @param images The set of images to be added.
+   * @param update Whether the map must be updated or not.
+   */
+  public synchronized void add(Set<MapillaryAbstractImage> images, boolean update) {
     for (MapillaryAbstractImage image : images) {
       add(image, update);
@@ -104,6 +108,5 @@
    * Adds a new listener.
    *
-   * @param lis
-   *          Listener to be added.
+   * @param lis Listener to be added.
    */
   public void addListener(MapillaryDataListener lis) {
@@ -115,6 +118,5 @@
    * ctrl + click)
    *
-   * @param image
-   *          The {@link MapillaryImage} object to be added.
+   * @param image The {@link MapillaryImage} object to be added.
    */
   public void addMultiSelectedImage(MapillaryAbstractImage image) {
@@ -133,8 +135,7 @@
    * selected images.
    *
-   * @param images
-   *          A List object containing the set of images to be added.
-   */
-  public void addMultiSelectedImage(List<MapillaryAbstractImage> images) {
+   * @param images A List object containing the set of images to be added.
+   */
+  public void addMultiSelectedImage(Set<MapillaryAbstractImage> images) {
     for (MapillaryAbstractImage image : images)
       if (!this.multiSelectedImages.contains(image)) {
@@ -151,10 +152,9 @@
    * and from its {@link MapillarySequence}.
    *
-   * @param image
-   *          The {@link MapillaryAbstractImage} that is going to be deleted.
+   * @param image The {@link MapillaryAbstractImage} that is going to be deleted.
    */
   public synchronized void remove(MapillaryAbstractImage image) {
     if (Main.main != null
-        && MapillaryMainDialog.getInstance().getImage() != null) {
+            && MapillaryMainDialog.getInstance().getImage() != null) {
       MapillaryMainDialog.getInstance().setImage(null);
       MapillaryMainDialog.getInstance().updateImage();
@@ -171,11 +171,11 @@
    * Removes a set of images from the database.
    *
-   * @param images
-   *          A {@link List} of {@link MapillaryAbstractImage} objects that are
-   *          going to be removed.
-   */
-  public synchronized void remove(List<MapillaryAbstractImage> images) {
-    for (MapillaryAbstractImage img : images)
+   * @param images A {@link List} of {@link MapillaryAbstractImage} objects that are
+   *               going to be removed.
+   */
+  public synchronized void remove(Set<MapillaryAbstractImage> images) {
+    for (MapillaryAbstractImage img : images) {
       remove(img);
+    }
   }
 
@@ -183,6 +183,5 @@
    * Removes a listener.
    *
-   * @param lis
-   *          Listener to be removed.
+   * @param lis Listener to be removed.
    */
   public void removeListener(MapillaryDataListener lis) {
@@ -193,6 +192,5 @@
    * Highlights the image under the cursor.
    *
-   * @param image
-   *          The image under the cursor.
+   * @param image The image under the cursor.
    */
   public void setHighlightedImage(MapillaryAbstractImage image) {
@@ -222,5 +220,5 @@
    * @return A List object containing all images.
    */
-  public synchronized List<MapillaryAbstractImage> getImages() {
+  public synchronized Set<MapillaryAbstractImage> getImages() {
     return this.images;
   }
@@ -248,7 +246,6 @@
    * nothing.
    *
-   * @throws IllegalStateException
-   *           if the selected image is null or the selected image doesn't
-   *           belong to a sequence.
+   * @throws IllegalStateException if the selected image is null or the selected image doesn't
+   *                               belong to a sequence.
    */
   public void selectNext() {
@@ -261,9 +258,7 @@
    * nothing.
    *
-   * @param moveToPicture
-   *          True if the view must me moved to the next picture.
-   * @throws IllegalStateException
-   *           if the selected image is null or the selected image doesn't
-   *           belong to a sequence.
+   * @param moveToPicture True if the view must me moved to the next picture.
+   * @throws IllegalStateException if the selected image is null or the selected image doesn't
+   *                               belong to a sequence.
    */
   public void selectNext(boolean moveToPicture) {
@@ -287,7 +282,6 @@
    * nothing.
    *
-   * @throws IllegalStateException
-   *           if the selected image is null or the selected image doesn't
-   *           belong to a sequence.
+   * @throws IllegalStateException if the selected image is null or the selected image doesn't
+   *                               belong to a sequence.
    */
   public void selectPrevious() {
@@ -301,9 +295,7 @@
    * the selected image doesn't belong to a sequence.
    *
-   * @param moveToPicture
-   *          True if the view must me moved to the previous picture.
-   * @throws IllegalStateException
-   *           if the selected image is null or the selected image doesn't
-   *           belong to a sequence.
+   * @param moveToPicture True if the view must me moved to the previous picture.
+   * @throws IllegalStateException if the selected image is null or the selected image doesn't
+   *                               belong to a sequence.
    */
   public void selectPrevious(boolean moveToPicture) {
@@ -325,7 +317,5 @@
    * Selects a new image.If the user does ctrl + click, this isn't triggered.
    *
-   * @param image
-   *          The MapillaryImage which is going to be selected
-   *
+   * @param image The MapillaryImage which is going to be selected
    */
   public void setSelectedImage(MapillaryAbstractImage image) {
@@ -337,8 +327,6 @@
    * can choose whether to center the view on the new image or not.
    *
-   * @param image
-   *          The {@link MapillaryImage} which is going to be selected.
-   * @param zoom
-   *          True if the view must be centered on the image; false otherwise.
+   * @param image The {@link MapillaryImage} which is going to be selected.
+   * @param zoom  True if the view must be centered on the image; false otherwise.
    */
   public void setSelectedImage(MapillaryAbstractImage image, boolean zoom) {
@@ -346,5 +334,7 @@
     this.selectedImage = image;
     this.multiSelectedImages.clear();
-    this.multiSelectedImages.add(image);
+    if (image != null) {
+      this.multiSelectedImages.add(image);
+    }
     if (image != null && Main.main != null && image instanceof MapillaryImage) {
       MapillaryImage mapillaryImage = (MapillaryImage) image;
@@ -369,5 +359,5 @@
 
   private void fireSelectedImageChanged(MapillaryAbstractImage oldImage,
-      MapillaryAbstractImage newImage) {
+                                        MapillaryAbstractImage newImage) {
     if (this.listeners.isEmpty())
       return;
@@ -383,5 +373,5 @@
    * @return A List object containing all the images selected.
    */
-  public List<MapillaryAbstractImage> getMultiSelectedImages() {
+  public Set<MapillaryAbstractImage> getMultiSelectedImages() {
     return this.multiSelectedImages;
   }
@@ -392,6 +382,6 @@
    * @param images the new image list (previously set images are completely replaced)
    */
-  public synchronized void setImages(List<MapillaryAbstractImage> images) {
-    this.images = new ArrayList<>(images);
+  public synchronized void setImages(Set<MapillaryAbstractImage> images) {
+    this.images = new ConcurrentSkipListSet<>(images);
   }
 
Index: /applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/MapillaryImage.java
===================================================================
--- /applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/MapillaryImage.java	(revision 31881)
+++ /applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/MapillaryImage.java	(revision 31882)
@@ -120,4 +120,11 @@
 
   @Override
+  public int compareTo(MapillaryAbstractImage image) {
+    if (image instanceof MapillaryImage)
+      return this.key.compareTo(((MapillaryImage) image).getKey());
+    return super.compareTo(image);
+  }
+
+  @Override
   public int hashCode() {
     return this.key.hashCode();
Index: /applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/MapillaryImportedImage.java
===================================================================
--- /applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/MapillaryImportedImage.java	(revision 31881)
+++ /applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/MapillaryImportedImage.java	(revision 31882)
@@ -100,4 +100,11 @@
 
   @Override
+  public int compareTo(MapillaryAbstractImage image) {
+    if (image instanceof MapillaryImage)
+      return this.file.compareTo(((MapillaryImportedImage) image).getFile());
+    return super.compareTo(image);
+  }
+
+  @Override
   public int hashCode() {
     return this.file.hashCode();
Index: /applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/MapillarySequence.java
===================================================================
--- /applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/MapillarySequence.java	(revision 31881)
+++ /applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/MapillarySequence.java	(revision 31882)
@@ -10,12 +10,17 @@
  * @author nokutu
  * @see MapillaryAbstractImage
- *
  */
 public class MapillarySequence {
-  /** The images in the sequence. */
+  /**
+   * The images in the sequence.
+   */
   private final List<MapillaryAbstractImage> images;
-  /** Unique identifier. Used only for {@link MapillaryImage} sequences. */
+  /**
+   * Unique identifier. Used only for {@link MapillaryImage} sequences.
+   */
   private String key;
-  /** Epoch time when the sequence was created */
+  /**
+   * Epoch time when the sequence was created
+   */
   private long createdAt;
 
@@ -31,8 +36,6 @@
    * Creates a sequence object with the given parameters.
    *
-   * @param key
-   *          The unique identifier of the sequence.
-   * @param createdAt
-   *          The date the sequence was created.
+   * @param key       The unique identifier of the sequence.
+   * @param createdAt The date the sequence was created.
    */
   public MapillarySequence(String key, long createdAt) {
@@ -45,6 +48,5 @@
    * Adds a new {@link MapillaryAbstractImage} object to the database.
    *
-   * @param image
-   *          The {@link MapillaryAbstractImage} object to be added
+   * @param image The {@link MapillaryAbstractImage} object to be added
    */
   public synchronized void add(MapillaryAbstractImage image) {
@@ -55,6 +57,5 @@
    * Adds a set of {@link MapillaryAbstractImage} objects to the database.
    *
-   * @param images
-   *          The set of {@link MapillaryAbstractImage} objects to be added.
+   * @param images The set of {@link MapillaryAbstractImage} objects to be added.
    */
   public synchronized void add(List<MapillaryAbstractImage> images) {
@@ -67,5 +68,4 @@
    *
    * @return A long containing the Epoch time when the sequence was captured.
-   *
    */
   public long getCreatedAt() {
@@ -78,6 +78,6 @@
    *
    * @return A {@link List} object containing all the
-   *         {@link MapillaryAbstractImage} objects that are part of the
-   *         sequence.
+   * {@link MapillaryAbstractImage} objects that are part of the
+   * sequence.
    */
   public List<MapillaryAbstractImage> getImages() {
@@ -89,6 +89,6 @@
    *
    * @return A {@code String} containing the unique identifier of the sequence.
-   *         null means that the sequence has been created locally for imported
-   *         images.
+   * null means that the sequence has been created locally for imported
+   * images.
    */
   public String getKey() {
@@ -100,18 +100,18 @@
    * {@link MapillaryAbstractImage} object.
    *
-   * @param image
-   *          The {@link MapillaryAbstractImage} object whose next image is
-   *          going to be returned.
+   * @param image The {@link MapillaryAbstractImage} object whose next image is
+   *              going to be returned.
    * @return The next {@link MapillaryAbstractImage} object in the sequence.
-   * @throws IllegalArgumentException
-   *           if the given {@link MapillaryAbstractImage} object doesn't belong
-   *           the this sequence.
+   * @throws IllegalArgumentException if the given {@link MapillaryAbstractImage} object doesn't belong
+   *                                  the this sequence.
    */
   public MapillaryAbstractImage next(MapillaryAbstractImage image) {
-    if (!this.images.contains(image))
+    int i = this.images.indexOf(image);
+    if (i == -1) {
       throw new IllegalArgumentException();
-    int i = this.images.indexOf(image);
-    if (i == this.images.size() - 1)
+    }
+    if (i == this.images.size() - 1) {
       return null;
+    }
     return this.images.get(i + 1);
   }
@@ -121,18 +121,18 @@
    * given {@link MapillaryAbstractImage} object.
    *
-   * @param image
-   *          The {@link MapillaryAbstractImage} object whose previous image is
-   *          going to be returned.
+   * @param image The {@link MapillaryAbstractImage} object whose previous image is
+   *              going to be returned.
    * @return The previous {@link MapillaryAbstractImage} object in the sequence.
-   * @throws IllegalArgumentException
-   *           if the given {@link MapillaryAbstractImage} object doesn't belong
-   *           the this sequence.
+   * @throws IllegalArgumentException if the given {@link MapillaryAbstractImage} object doesn't belong
+   *                                  the this sequence.
    */
   public MapillaryAbstractImage previous(MapillaryAbstractImage image) {
-    if (!this.images.contains(image))
+    int i = this.images.indexOf(image);
+    if (i == -1) {
       throw new IllegalArgumentException();
-    int i = this.images.indexOf(image);
-    if (i == 0)
+    }
+    if (i == 0) {
       return null;
+    }
     return this.images.get(i - 1);
   }
@@ -141,6 +141,5 @@
    * Removes a {@link MapillaryAbstractImage} object from the database.
    *
-   * @param image
-   *          The {@link MapillaryAbstractImage} object to be removed.
+   * @param image The {@link MapillaryAbstractImage} object to be removed.
    */
   public void remove(MapillaryAbstractImage image) {
Index: /applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/actions/MapillaryExportAction.java
===================================================================
--- /applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/actions/MapillaryExportAction.java	(revision 31881)
+++ /applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/actions/MapillaryExportAction.java	(revision 31882)
@@ -11,4 +11,6 @@
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ConcurrentSkipListSet;
 
 import javax.swing.JButton;
@@ -74,5 +76,5 @@
         export(MapillaryLayer.getInstance().getData().getImages());
       } else if (this.dialog.group.isSelected(this.dialog.sequence.getModel())) {
-        ArrayList<MapillaryAbstractImage> images = new ArrayList<>();
+        Set<MapillaryAbstractImage> images = new ConcurrentSkipListSet<>();
         for (MapillaryAbstractImage image : MapillaryLayer.getInstance()
             .getData().getMultiSelectedImages())
@@ -110,5 +112,5 @@
    *          The set of images to be exported.
    */
-  public void export(List<MapillaryAbstractImage> images) {
+  public void export(Set<MapillaryAbstractImage> images) {
     Main.worker.submit(new Thread(new MapillaryExportManager(images,
         this.dialog.chooser.getSelectedFile().toString())));
Index: /applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/actions/MapillaryImportAction.java
===================================================================
--- /applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/actions/MapillaryImportAction.java	(revision 31881)
+++ /applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/actions/MapillaryImportAction.java	(revision 31882)
@@ -6,8 +6,11 @@
 import java.awt.event.ActionEvent;
 import java.awt.event.KeyEvent;
+import java.beans.beancontext.BeanContextChildComponentProxy;
 import java.io.File;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ConcurrentSkipListSet;
 
 import javax.swing.JFileChooser;
@@ -67,5 +70,5 @@
     chooser.setMultiSelectionEnabled(true);
     if (chooser.showOpenDialog(Main.parent) == JFileChooser.APPROVE_OPTION) {
-      List<MapillaryAbstractImage> images = new ArrayList<>();
+      Set<MapillaryAbstractImage> images = new ConcurrentSkipListSet<>();
       for (File file : chooser.getSelectedFiles()) {
         Main.pref.put("mapillary.start-directory", file.getParent());
Index: /applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/actions/MapillaryImportIntoSequenceAction.java
===================================================================
--- /applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/actions/MapillaryImportIntoSequenceAction.java	(revision 31881)
+++ /applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/actions/MapillaryImportIntoSequenceAction.java	(revision 31882)
@@ -8,8 +8,6 @@
 import java.io.File;
 import java.io.IOException;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.LinkedList;
-import java.util.List;
+import java.util.*;
+import java.util.concurrent.ConcurrentSkipListSet;
 
 import javax.swing.JFileChooser;
@@ -55,5 +53,5 @@
   @Override
   public void actionPerformed(ActionEvent arg0) {
-    this.images = new LinkedList<>();
+    this.images = new ArrayList<>();
 
     JFileChooser chooser = new JFileChooser();
@@ -99,5 +97,5 @@
       }
       joinImages();
-      MapillaryRecord.getInstance().addCommand(new CommandImport(this.images));
+      MapillaryRecord.getInstance().addCommand(new CommandImport(new ConcurrentSkipListSet(images)));
     }
     MapillaryUtils.showAllPictures();
Index: /applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/gui/MapillaryMainDialog.java
===================================================================
--- /applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/gui/MapillaryMainDialog.java	(revision 31881)
+++ /applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/gui/MapillaryMainDialog.java	(revision 31882)
@@ -39,4 +39,5 @@
 import org.openstreetmap.josm.plugins.mapillary.actions.WalkListener;
 import org.openstreetmap.josm.plugins.mapillary.actions.WalkThread;
+import org.openstreetmap.josm.plugins.mapillary.cache.CacheUtils;
 import org.openstreetmap.josm.plugins.mapillary.cache.MapillaryCache;
 import org.openstreetmap.josm.tools.ImageProvider;
@@ -47,8 +48,7 @@
  *
  * @author nokutu
- *
  */
 public class MapillaryMainDialog extends ToggleDialog implements
-    ICachedLoaderListener, MapillaryDataListener {
+        ICachedLoaderListener, MapillaryDataListener {
 
   private static final long serialVersionUID = 6856496736429480600L;
@@ -62,8 +62,12 @@
   private final SideButton nextButton = new SideButton(new NextPictureAction());
   private final SideButton previousButton = new SideButton(
-      new PreviousPictureAction());
-  /** Button used to jump to the image following the red line */
+          new PreviousPictureAction());
+  /**
+   * Button used to jump to the image following the red line
+   */
   public final SideButton redButton = new SideButton(new RedAction());
-  /** Button used to jump to the image following the blue line */
+  /**
+   * Button used to jump to the image following the blue line
+   */
   public final SideButton blueButton = new SideButton(new BlueAction());
 
@@ -76,10 +80,13 @@
    *
    * @author nokutu
-   *
    */
   public enum MODE {
-    /** Standard mode to view pictures. */
+    /**
+     * Standard mode to view pictures.
+     */
     NORMAL,
-    /** Mode when in walk. */
+    /**
+     * Mode when in walk.
+     */
     WALK;
   }
@@ -87,5 +94,7 @@
   private JPanel buttonsPanel;
 
-  /** Object containing the shown image and that handles zoom and drag */
+  /**
+   * Object containing the shown image and that handles zoom and drag
+   */
   public MapillaryImageDisplay mapillaryImageDisplay;
 
@@ -95,7 +104,7 @@
   private MapillaryMainDialog() {
     super(tr(BASE_TITLE), "mapillary.png", tr("Open Mapillary window"),
-        Shortcut.registerShortcut(tr("Mapillary dialog"),
-            tr("Open Mapillary main dialog"), KeyEvent.VK_M, Shortcut.NONE),
-        200, false, MapillaryPreferenceSetting.class);
+            Shortcut.registerShortcut(tr("Mapillary dialog"),
+                    tr("Open Mapillary main dialog"), KeyEvent.VK_M, Shortcut.NONE),
+            200, false, MapillaryPreferenceSetting.class);
     addShortcuts();
     this.mapillaryImageDisplay = new MapillaryImageDisplay();
@@ -105,7 +114,7 @@
 
     createLayout(
-        this.mapillaryImageDisplay,
-        Arrays.asList(new SideButton[] {this.blueButton, this.previousButton, this.nextButton, this.redButton}),
-        Main.pref.getBoolean("mapillary.reverse-buttons"));
+            this.mapillaryImageDisplay,
+            Arrays.asList(new SideButton[]{this.blueButton, this.previousButton, this.nextButton, this.redButton}),
+            Main.pref.getBoolean("mapillary.reverse-buttons"));
     disableAllButtons();
 
@@ -117,15 +126,15 @@
   private void addShortcuts() {
     this.nextButton.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
-        KeyStroke.getKeyStroke("PAGE_DOWN"), "next");
+            KeyStroke.getKeyStroke("PAGE_DOWN"), "next");
     this.nextButton.getActionMap().put("next", new NextPictureAction());
     this.previousButton.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
-        KeyStroke.getKeyStroke("PAGE_UP"), "previous");
+            KeyStroke.getKeyStroke("PAGE_UP"), "previous");
     this.previousButton.getActionMap().put("previous",
-        new PreviousPictureAction());
+            new PreviousPictureAction());
     this.blueButton.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
-        KeyStroke.getKeyStroke("control PAGE_UP"), "blue");
+            KeyStroke.getKeyStroke("control PAGE_UP"), "blue");
     this.blueButton.getActionMap().put("blue", new BlueAction());
     this.redButton.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
-        KeyStroke.getKeyStroke("control PAGE_DOWN"), "red");
+            KeyStroke.getKeyStroke("control PAGE_DOWN"), "red");
     this.redButton.getActionMap().put("red", new RedAction());
   }
@@ -145,6 +154,5 @@
    * Sets a new mode for the dialog.
    *
-   * @param mode
-   *          The mode to be set.
+   * @param mode The mode to be set.
    */
   public void setMode(MODE mode) {
@@ -152,14 +160,14 @@
       case WALK:
         createLayout(
-            this.mapillaryImageDisplay,
-            Arrays.asList(new SideButton[] {playButton, pauseButton, stopButton}),
-            Main.pref.getBoolean("mapillary.reverse-buttons"));
+                this.mapillaryImageDisplay,
+                Arrays.asList(new SideButton[]{playButton, pauseButton, stopButton}),
+                Main.pref.getBoolean("mapillary.reverse-buttons"));
         break;
       case NORMAL:
       default:
         createLayout(
-            this.mapillaryImageDisplay,
-            Arrays.asList(new SideButton[] {blueButton, previousButton, nextButton, redButton}),
-            Main.pref.getBoolean("mapillary.reverse-buttons"));
+                this.mapillaryImageDisplay,
+                Arrays.asList(new SideButton[]{blueButton, previousButton, nextButton, redButton}),
+                Main.pref.getBoolean("mapillary.reverse-buttons"));
         break;
     }
@@ -189,7 +197,6 @@
    * MapillaryImageDisplay object.
    *
-   * @param fullQuality
-   *          If the full quality picture must be downloaded or just the
-   *          thumbnail.
+   * @param fullQuality If the full quality picture must be downloaded or just the
+   *                    thumbnail.
    */
   public synchronized void updateImage(boolean fullQuality) {
@@ -243,5 +250,5 @@
           this.thumbnailCache.cancelOutstandingTasks();
         this.thumbnailCache = new MapillaryCache(mapillaryImage.getKey(),
-            MapillaryCache.Type.THUMBNAIL);
+                MapillaryCache.Type.THUMBNAIL);
         try {
           this.thumbnailCache.submit(this, false);
@@ -251,9 +258,10 @@
 
         // Downloads the full resolution image.
-        if (fullQuality) {
+        if (fullQuality || new MapillaryCache(mapillaryImage.getKey(),
+                MapillaryCache.Type.FULL_IMAGE).get() != null) {
           if (this.imageCache != null)
             this.imageCache.cancelOutstandingTasks();
           this.imageCache = new MapillaryCache(mapillaryImage.getKey(),
-              MapillaryCache.Type.FULL_IMAGE);
+                  MapillaryCache.Type.FULL_IMAGE);
           try {
             this.imageCache.submit(this, false);
@@ -277,5 +285,7 @@
   }
 
-  /** Disables all the buttons in the dialog */
+  /**
+   * Disables all the buttons in the dialog
+   */
   private void disableAllButtons() {
     this.nextButton.setEnabled(false);
@@ -289,6 +299,5 @@
    * Sets a new MapillaryImage to be shown.
    *
-   * @param image
-   *          The image to be shown.
+   * @param image The image to be shown.
    */
   public synchronized void setImage(MapillaryAbstractImage image) {
@@ -338,5 +347,4 @@
    *
    * @author nokutu
-   *
    */
   private static class NextPictureAction extends AbstractAction {
@@ -362,5 +370,4 @@
    *
    * @author nokutu
-   *
    */
   private class PreviousPictureAction extends AbstractAction {
@@ -374,5 +381,5 @@
       putValue(NAME, tr("Previous picture"));
       putValue(SHORT_DESCRIPTION,
-          tr("Shows the previous picture in the sequence"));
+              tr("Shows the previous picture in the sequence"));
     }
 
@@ -387,5 +394,4 @@
    *
    * @author nokutu
-   *
    */
   private class RedAction extends AbstractAction {
@@ -399,5 +405,5 @@
       putValue(NAME, tr("Jump to red"));
       putValue(SHORT_DESCRIPTION,
-          tr("Jumps to the picture at the other side of the red line"));
+              tr("Jumps to the picture at the other side of the red line"));
     }
 
@@ -406,5 +412,5 @@
       if (MapillaryMainDialog.getInstance().getImage() != null) {
         MapillaryLayer.getInstance().getData()
-            .setSelectedImage(MapillaryLayer.getInstance().getRed(), true);
+                .setSelectedImage(MapillaryLayer.getInstance().getRed(), true);
       }
     }
@@ -415,5 +421,4 @@
    *
    * @author nokutu
-   *
    */
   private class BlueAction extends AbstractAction {
@@ -427,5 +432,5 @@
       putValue(NAME, tr("Jump to blue"));
       putValue(SHORT_DESCRIPTION,
-          tr("Jumps to the picture at the other side of the blue line"));
+              tr("Jumps to the picture at the other side of the blue line"));
     }
 
@@ -434,5 +439,5 @@
       if (MapillaryMainDialog.getInstance().getImage() != null) {
         MapillaryLayer.getInstance().getData()
-            .setSelectedImage(MapillaryLayer.getInstance().getBlue(), true);
+                .setSelectedImage(MapillaryLayer.getInstance().getBlue(), true);
       }
     }
@@ -528,5 +533,5 @@
   @Override
   public void loadingFinished(final CacheEntry data,
-      final CacheEntryAttributes attributes, final LoadResult result) {
+                              final CacheEntryAttributes attributes, final LoadResult result) {
     if (!SwingUtilities.isEventDispatchThread()) {
       SwingUtilities.invokeLater(new Runnable() {
@@ -543,7 +548,7 @@
         }
         if (
-            this.mapillaryImageDisplay.getImage() == null
-            || img.getHeight() > this.mapillaryImageDisplay.getImage().getHeight()
-        ) {
+                this.mapillaryImageDisplay.getImage() == null
+                        || img.getHeight() > this.mapillaryImageDisplay.getImage().getHeight()
+                ) {
           this.mapillaryImageDisplay.setImage(img);
         }
@@ -557,14 +562,11 @@
    * Creates the layout of the dialog.
    *
-   * @param data
-   *          The content of the dialog
-   * @param buttons
-   *          The buttons where you can click
-   * @param reverse
-   *          {@code true} if the buttons should go at the top; {@code false}
-   *          otherwise.
+   * @param data    The content of the dialog
+   * @param buttons The buttons where you can click
+   * @param reverse {@code true} if the buttons should go at the top; {@code false}
+   *                otherwise.
    */
   public void createLayout(Component data, List<SideButton> buttons,
-      boolean reverse) {
+                           boolean reverse) {
     this.removeAll();
     JPanel panel = new JPanel();
@@ -575,6 +577,6 @@
       if (!buttons.isEmpty() && buttons.get(0) != null) {
         final JPanel buttonRowPanel = new JPanel(Main.pref.getBoolean(
-            "dialog.align.left", false) ? new FlowLayout(FlowLayout.LEFT)
-            : new GridLayout(1, buttons.size()));
+                "dialog.align.left", false) ? new FlowLayout(FlowLayout.LEFT)
+                : new GridLayout(1, buttons.size()));
         this.buttonsPanel.add(buttonRowPanel);
         for (SideButton button : buttons)
Index: /applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/history/commands/CommandDelete.java
===================================================================
--- /applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/history/commands/CommandDelete.java	(revision 31881)
+++ /applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/history/commands/CommandDelete.java	(revision 31882)
@@ -7,4 +7,5 @@
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 import org.openstreetmap.josm.plugins.mapillary.MapillaryAbstractImage;
@@ -27,5 +28,5 @@
    *          The set of images that are going to be deleted.
    */
-  public CommandDelete(List<MapillaryAbstractImage> images) {
+  public CommandDelete(Set<MapillaryAbstractImage> images) {
     super(images);
   }
@@ -52,6 +53,5 @@
   @Override
   public void undo() {
-    for (int i = this.images.size() - 1; i >= 0; i--) {
-      MapillaryAbstractImage img = this.images.get(i);
+    for (MapillaryAbstractImage img : images) {
       MapillaryLayer.getInstance().getData().add(img);
       img.getSequence().getImages().add(this.changesHash.get(img), img);
Index: /applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/history/commands/CommandImport.java
===================================================================
--- /applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/history/commands/CommandImport.java	(revision 31881)
+++ /applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/history/commands/CommandImport.java	(revision 31882)
@@ -5,4 +5,5 @@
 
 import java.util.List;
+import java.util.Set;
 
 import org.openstreetmap.josm.Main;
@@ -26,5 +27,5 @@
    *          sequence or not.
    */
-  public CommandImport(List<MapillaryAbstractImage> images) {
+  public CommandImport(Set<MapillaryAbstractImage> images) {
     super(images);
   }
Index: /applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/history/commands/CommandJoin.java
===================================================================
--- /applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/history/commands/CommandJoin.java	(revision 31881)
+++ /applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/history/commands/CommandJoin.java	(revision 31882)
@@ -5,4 +5,5 @@
 
 import java.util.List;
+import java.util.concurrent.ConcurrentSkipListSet;
 
 import org.openstreetmap.josm.plugins.mapillary.MapillaryAbstractImage;
@@ -17,4 +18,7 @@
 public class CommandJoin extends MapillaryExecutableCommand {
 
+  private MapillaryAbstractImage a;
+  private MapillaryAbstractImage b;
+
   /**
    * Main constructor.
@@ -27,5 +31,7 @@
    */
   public CommandJoin(List<MapillaryAbstractImage> images) {
-    super(images);
+    super(new ConcurrentSkipListSet(images));
+    a = images.get(0);
+    b = images.get(1);
     if (images.size() != 2)
       throw new IllegalArgumentException();
@@ -39,10 +45,10 @@
   @Override
   public void undo() {
-    MapillaryUtils.unjoin(this.images.get(0), this.images.get(1));
+    MapillaryUtils.unjoin(a, b);
   }
 
   @Override
   public void redo() {
-    MapillaryUtils.join(this.images.get(0), this.images.get(1));
+    MapillaryUtils.join(a, b);
   }
 
Index: /applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/history/commands/CommandMove.java
===================================================================
--- /applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/history/commands/CommandMove.java	(revision 31881)
+++ /applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/history/commands/CommandMove.java	(revision 31882)
@@ -5,4 +5,5 @@
 
 import java.util.List;
+import java.util.Set;
 
 import org.openstreetmap.josm.Main;
@@ -29,6 +30,6 @@
    *          How much the y coordinate increases.
    */
-  public CommandMove(List<MapillaryAbstractImage> images, double x,
-      double y) {
+  public CommandMove(Set<MapillaryAbstractImage> images, double x,
+                     double y) {
     super(images);
     this.x = x;
Index: /applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/history/commands/CommandTurn.java
===================================================================
--- /applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/history/commands/CommandTurn.java	(revision 31881)
+++ /applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/history/commands/CommandTurn.java	(revision 31882)
@@ -5,4 +5,5 @@
 
 import java.util.List;
+import java.util.Set;
 
 import org.openstreetmap.josm.Main;
@@ -26,5 +27,5 @@
    *          How much the images turn.
    */
-  public CommandTurn(List<MapillaryAbstractImage> images, double ca) {
+  public CommandTurn(Set<MapillaryAbstractImage> images, double ca) {
     super(images);
     this.ca = ca;
Index: /applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/history/commands/CommandUnjoin.java
===================================================================
--- /applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/history/commands/CommandUnjoin.java	(revision 31881)
+++ /applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/history/commands/CommandUnjoin.java	(revision 31882)
@@ -5,4 +5,6 @@
 
 import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ConcurrentSkipListSet;
 
 import org.openstreetmap.josm.plugins.mapillary.MapillaryAbstractImage;
@@ -17,4 +19,7 @@
 public class CommandUnjoin extends MapillaryExecutableCommand {
 
+  private MapillaryAbstractImage a;
+  private MapillaryAbstractImage b;
+
   /**
    * Main constructor.
@@ -27,5 +32,7 @@
    */
   public CommandUnjoin(List<MapillaryAbstractImage> images) {
-    super(images);
+    super(new ConcurrentSkipListSet(images));
+    a = images.get(0);
+    b = images.get(1);
     if (images.size() != 2)
       throw new IllegalArgumentException();
@@ -39,10 +46,10 @@
   @Override
   public void undo() {
-    MapillaryUtils.join(this.images.get(0), this.images.get(1));
+    MapillaryUtils.join(a, b);
   }
 
   @Override
   public void redo() {
-    MapillaryUtils.unjoin(this.images.get(0), this.images.get(1));
+    MapillaryUtils.unjoin(a, b);
   }
 
Index: /applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/history/commands/MapillaryCommand.java
===================================================================
--- /applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/history/commands/MapillaryCommand.java	(revision 31881)
+++ /applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/history/commands/MapillaryCommand.java	(revision 31882)
@@ -4,4 +4,6 @@
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ConcurrentSkipListSet;
 
 import org.openstreetmap.josm.plugins.mapillary.MapillaryAbstractImage;
@@ -15,5 +17,5 @@
 public abstract class MapillaryCommand {
   /** Set of {@link MapillaryAbstractImage} objects affected by the command */
-  public List<MapillaryAbstractImage> images;
+  public Set<MapillaryAbstractImage> images;
 
   /**
@@ -23,6 +25,6 @@
    *          The images that are affected by the command.
    */
-  public MapillaryCommand(List<MapillaryAbstractImage> images) {
-    this.images = new ArrayList<>(images);
+  public MapillaryCommand(Set<MapillaryAbstractImage> images) {
+    this.images = new ConcurrentSkipListSet<>(images);
   }
 
Index: /applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/history/commands/MapillaryExecutableCommand.java
===================================================================
--- /applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/history/commands/MapillaryExecutableCommand.java	(revision 31881)
+++ /applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/history/commands/MapillaryExecutableCommand.java	(revision 31882)
@@ -3,4 +3,5 @@
 
 import java.util.List;
+import java.util.Set;
 
 import org.openstreetmap.josm.plugins.mapillary.MapillaryAbstractImage;
@@ -20,5 +21,5 @@
    *          The set of images affected by the command.
    */
-  public MapillaryExecutableCommand(List<MapillaryAbstractImage> images) {
+  public MapillaryExecutableCommand(Set<MapillaryAbstractImage> images) {
     super(images);
   }
Index: /applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/io/download/MapillarySequenceDownloadThread.java
===================================================================
--- /applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/io/download/MapillarySequenceDownloadThread.java	(revision 31881)
+++ /applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/io/download/MapillarySequenceDownloadThread.java	(revision 31882)
@@ -7,4 +7,5 @@
 import java.util.ArrayList;
 import java.util.List;
+import java.util.concurrent.ConcurrentSkipListSet;
 import java.util.concurrent.ExecutorService;
 
@@ -38,7 +39,7 @@
    * Main constructor.
    *
-   * @param ex {@link ExecutorService} executing this thread.
+   * @param ex     {@link ExecutorService} executing this thread.
    * @param bounds The bounds inside which the sequences should be downloaded
-   * @param page the pagenumber of the results that should be retrieved
+   * @param page   the pagenumber of the results that should be retrieved
    */
   public MapillarySequenceDownloadThread(ExecutorService ex, Bounds bounds, int page) {
@@ -51,8 +52,8 @@
   public void run() {
     try (
-      BufferedReader br = new BufferedReader(new InputStreamReader(
-          MapillaryURL.searchSequenceURL(bounds, page).openStream(),
-          "UTF-8"
-      ));
+            BufferedReader br = new BufferedReader(new InputStreamReader(
+                    MapillaryURL.searchSequenceURL(bounds, page).openStream(),
+                    "UTF-8"
+            ));
     ) {
       JsonObject jsonall = Json.createReader(br).readObject();
@@ -71,7 +72,7 @@
           try {
             images.add(new MapillaryImage(keys.getString(j), coords
-                .getJsonArray(j).getJsonNumber(1).doubleValue(), coords
-                .getJsonArray(j).getJsonNumber(0).doubleValue(), cas
-                .getJsonNumber(j).doubleValue()));
+                    .getJsonArray(j).getJsonNumber(1).doubleValue(), coords
+                    .getJsonArray(j).getJsonNumber(0).doubleValue(), cas
+                    .getJsonNumber(j).doubleValue()));
           } catch (IndexOutOfBoundsException e) {
             Main.warn("Mapillary bug at " + MapillaryURL.searchSequenceURL(bounds, page));
@@ -82,5 +83,5 @@
           break;
         MapillarySequence sequence = new MapillarySequence(
-            jsonobj.getString("key"), jsonobj.getJsonNumber("captured_at")
+                jsonobj.getString("key"), jsonobj.getJsonNumber("captured_at")
                 .longValue());
         List<MapillaryImage> finalImages = new ArrayList<>(images);
@@ -97,10 +98,11 @@
                 // The image in finalImages is substituted by the one in the
                 // database, as they represent the same picture.
-                img = (MapillaryImage) MapillaryLayer.getInstance().getData().getImages()
-                    .get(MapillaryLayer.getInstance().getData().getImages().indexOf(img));
+                for (MapillaryAbstractImage source : MapillaryLayer.getInstance().getData().getImages()) {
+                  if (source.equals(img)) {
+                    img = (MapillaryImage) source;
+                  }
+                }
                 sequence.add(img);
-                ((MapillaryImage) MapillaryLayer.getInstance().getData().getImages()
-                    .get(MapillaryLayer.getInstance().getData().getImages().indexOf(img)))
-                    .setSequence(sequence);
+                img.setSequence(sequence);
                 finalImages.set(finalImages.indexOf(img), img);
               } else {
@@ -112,5 +114,5 @@
         }
 
-        MapillaryLayer.getInstance().getData().add(new ArrayList<MapillaryAbstractImage>(finalImages), false);
+        MapillaryLayer.getInstance().getData().add(new ConcurrentSkipListSet(finalImages), false);
       }
     } catch (IOException e) {
Index: /applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/io/export/MapillaryExportManager.java
===================================================================
--- /applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/io/export/MapillaryExportManager.java	(revision 31881)
+++ /applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/io/export/MapillaryExportManager.java	(revision 31882)
@@ -7,4 +7,5 @@
 import java.io.IOException;
 import java.util.List;
+import java.util.Set;
 import java.util.concurrent.ArrayBlockingQueue;
 import java.util.concurrent.ThreadPoolExecutor;
@@ -38,5 +39,5 @@
 
   private final int amount;
-  private List<MapillaryAbstractImage> images;
+  private Set<MapillaryAbstractImage> images;
   private String path;
 
@@ -52,5 +53,5 @@
    *          Export path.
    */
-  public MapillaryExportManager(List<MapillaryAbstractImage> images, String path) {
+  public MapillaryExportManager(Set<MapillaryAbstractImage> images, String path) {
     super(tr("Downloading") + "...", new PleaseWaitProgressMonitor(
         "Exporting Mapillary Images"), true);
Index: /applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/mode/SelectMode.java
===================================================================
--- /applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/mode/SelectMode.java	(revision 31881)
+++ /applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/mode/SelectMode.java	(revision 31882)
@@ -9,4 +9,5 @@
 import java.awt.event.MouseEvent;
 import java.util.ArrayList;
+import java.util.concurrent.ConcurrentSkipListSet;
 
 import org.openstreetmap.josm.Main;
@@ -29,5 +30,4 @@
  *
  * @author nokutu
- *
  */
 public class SelectMode extends AbstractMode {
@@ -54,14 +54,14 @@
     MapillaryAbstractImage closest = getClosest(e.getPoint());
     if (!(Main.map.mapView.getActiveLayer() instanceof MapillaryLayer)
-        && closest != null && Main.map.mapMode == Main.map.mapModeSelect) {
+            && closest != null && Main.map.mapMode == Main.map.mapModeSelect) {
       this.lastClicked = this.closest;
       this.data.setSelectedImage(closest);
       return;
     } else if (Main.map.mapView.getActiveLayer() != MapillaryLayer
-        .getInstance())
+            .getInstance())
       return;
     // Double click
     if (e.getClickCount() == 2 && this.data.getSelectedImage() != null
-        && closest != null) {
+            && closest != null) {
       for (MapillaryAbstractImage img : closest.getSequence().getImages()) {
         this.data.addMultiSelectedImage(img);
@@ -71,24 +71,24 @@
     this.lastClicked = this.closest;
     this.closest = closest;
-    if (this.data.getMultiSelectedImages().contains(closest))
+    if (closest != null && this.data.getMultiSelectedImages().contains(closest))
       return;
     // ctrl+click
     if (e.getModifiers() == (InputEvent.BUTTON1_MASK | InputEvent.CTRL_MASK)
-        && closest != null)
+            && closest != null)
       this.data.addMultiSelectedImage(closest);
-    // shift + click
+      // shift + click
     else if (e.getModifiers() == (InputEvent.BUTTON1_MASK | InputEvent.SHIFT_MASK)
-        && this.lastClicked instanceof MapillaryImage) {
+            && this.lastClicked instanceof MapillaryImage) {
       if (this.closest != null && this.lastClicked != null
-          && this.closest.getSequence() == (this.lastClicked).getSequence()) {
+              && this.closest.getSequence() == (this.lastClicked).getSequence()) {
         int i = this.closest.getSequence().getImages().indexOf(this.closest);
         int j = this.lastClicked.getSequence().getImages()
-            .indexOf(this.lastClicked);
+                .indexOf(this.lastClicked);
         if (i < j)
-          this.data.addMultiSelectedImage(new ArrayList<>(this.closest.getSequence()
-              .getImages().subList(i, j + 1)));
+          this.data.addMultiSelectedImage(new ConcurrentSkipListSet(this.closest.getSequence()
+                  .getImages().subList(i, j + 1)));
         else
-          this.data.addMultiSelectedImage(new ArrayList<>(this.closest.getSequence()
-              .getImages().subList(j, i + 1)));
+          this.data.addMultiSelectedImage(new ConcurrentSkipListSet(this.closest.getSequence()
+                  .getImages().subList(j, i + 1)));
       }
       // click
@@ -99,6 +99,7 @@
   @Override
   public void mouseDragged(MouseEvent e) {
-    if (Main.map.mapView.getActiveLayer() != MapillaryLayer.getInstance())
-      return;
+    if (Main.map.mapView.getActiveLayer() != MapillaryLayer.getInstance()) {
+      return;
+    }
 
     if (!Main.pref.getBoolean("mapillary.developer"))
@@ -118,9 +119,9 @@
       } else if (this.lastButton == MouseEvent.BUTTON1 && e.isShiftDown()) {
         this.closest.turn(Math.toDegrees(Math.atan2((e.getX() - this.start.x),
-            -(e.getY() - this.start.y)))
-            - this.closest.getTempCa());
+                -(e.getY() - this.start.y)))
+                - this.closest.getTempCa());
         for (MapillaryAbstractImage img : this.data.getMultiSelectedImages()) {
           img.turn(Math.toDegrees(Math.atan2((e.getX() - this.start.x),
-              -(e.getY() - this.start.y))) - this.closest.getTempCa());
+                  -(e.getY() - this.start.y))) - this.closest.getTempCa());
         }
         Main.map.repaint();
@@ -137,11 +138,11 @@
       double to = this.data.getSelectedImage().getCa();
       this.record.addCommand(new CommandTurn(this.data.getMultiSelectedImages(), to
-          - from));
+              - from));
     } else if (this.data.getSelectedImage().getTempLatLon() != this.data
-        .getSelectedImage().getLatLon()) {
+            .getSelectedImage().getLatLon()) {
       LatLon from = this.data.getSelectedImage().getTempLatLon();
       LatLon to = this.data.getSelectedImage().getLatLon();
       this.record.addCommand(new CommandMove(this.data.getMultiSelectedImages(), to
-          .getX() - from.getX(), to.getY() - from.getY()));
+              .getX() - from.getX(), to.getY() - from.getY()));
     }
     for (MapillaryAbstractImage img : this.data.getMultiSelectedImages()) {
@@ -156,26 +157,28 @@
   @Override
   public void mouseMoved(MouseEvent e) {
+    if (Main.map.mapView.getActiveLayer() instanceof OsmDataLayer
+            && Main.map.mapMode != Main.map.mapModeSelect) {
+      return;
+    }
     MapillaryAbstractImage closestTemp = getClosest(e.getPoint());
-    if (Main.map.mapView.getActiveLayer() instanceof OsmDataLayer
-        && Main.map.mapMode != Main.map.mapModeSelect)
-      return;
+
     if (closestTemp != null
-        && Main.map.mapView.getActiveLayer() instanceof OsmDataLayer
-        && !this.imageHighlighted) {
+            && Main.map.mapView.getActiveLayer() instanceof OsmDataLayer
+            && !this.imageHighlighted) {
       Main.map.mapMode.putValue("active", Boolean.FALSE);
       this.imageHighlighted = true;
 
     } else if (closestTemp == null
-        && Main.map.mapView.getActiveLayer() instanceof OsmDataLayer
-        && this.imageHighlighted && this.nothingHighlighted) {
+            && Main.map.mapView.getActiveLayer() instanceof OsmDataLayer
+            && this.imageHighlighted && this.nothingHighlighted) {
       this.nothingHighlighted = false;
       Main.map.mapMode.putValue("active", Boolean.TRUE);
 
     } else if (this.imageHighlighted && !this.nothingHighlighted
-        && Main.map.mapView.getEditLayer().data != null
-        && Main.map.mapView.getActiveLayer() instanceof OsmDataLayer) {
+            && Main.map.mapView.getEditLayer().data != null
+            && Main.map.mapView.getActiveLayer() instanceof OsmDataLayer) {
 
       for (OsmPrimitive primivitive : Main.map.mapView.getEditLayer().data
-          .allPrimitives()) {
+              .allPrimitives()) {
         primivitive.setHighlighted(false);
       }
Index: /applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/oauth/UploadUtils.java
===================================================================
--- /applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/oauth/UploadUtils.java	(revision 31881)
+++ /applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/oauth/UploadUtils.java	(revision 31882)
@@ -10,6 +10,8 @@
 import java.util.HashMap;
 import java.util.List;
+import java.util.Set;
 import java.util.UUID;
 import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.ConcurrentSkipListSet;
 import java.util.concurrent.ThreadPoolExecutor;
 import java.util.concurrent.TimeUnit;
@@ -52,26 +54,31 @@
  *
  * @author nokutu
- *
  */
 public class UploadUtils {
 
-  /** Required keys for POST */
-  private static final String[] keys = { "key", "AWSAccessKeyId", "acl",
-      "policy", "signature", "Content-Type" };
-
-  /** Mapillary upload URL */
+  /**
+   * Required keys for POST
+   */
+  private static final String[] keys = {"key", "AWSAccessKeyId", "acl",
+          "policy", "signature", "Content-Type"};
+
+  /**
+   * Mapillary upload URL
+   */
   private static final String UPLOAD_URL = "https://s3-eu-west-1.amazonaws.com/mapillary.uploads.manual.images";
 
-  /** Count to name temporal files. */
+  /**
+   * Count to name temporal files.
+   */
   private static int c;
 
   private static class SequenceUploadThread extends Thread {
-    private final List<MapillaryAbstractImage> images;
+    private final Set<MapillaryAbstractImage> images;
     private final UUID uuid;
     private final boolean delete;
     private final ThreadPoolExecutor ex;
 
-    private SequenceUploadThread(List<MapillaryAbstractImage> images,
-        boolean delete) {
+    private SequenceUploadThread(Set<MapillaryAbstractImage> images,
+                                 boolean delete) {
       this.images = images;
       this.uuid = UUID.randomUUID();
@@ -104,7 +111,8 @@
       if (this.delete)
         MapillaryRecord.getInstance()
-            .addCommand(new CommandDelete(this.images));
-    }
-  }
+                .addCommand(new CommandDelete(images));
+    }
+  }
+
   private static class SingleUploadThread extends Thread {
 
@@ -129,15 +137,12 @@
    * @param image
    * @return A File object containing the picture and an updated version of the
-   *         EXIF tags.
-   * @throws ImageReadException
-   *           if there are errors reading the image from the file.
-   * @throws IOException
-   *           if there are errors getting the metadata from the file or writing
-   *           the output.
-   * @throws ImageWriteException
-   *           if there are errors writing the image in the file.
+   * EXIF tags.
+   * @throws ImageReadException  if there are errors reading the image from the file.
+   * @throws IOException         if there are errors getting the metadata from the file or writing
+   *                             the output.
+   * @throws ImageWriteException if there are errors writing the image in the file.
    */
   public static File updateFile(MapillaryImportedImage image)
-      throws ImageReadException, IOException, ImageWriteException {
+          throws ImageReadException, IOException, ImageWriteException {
     TiffOutputSet outputSet = null;
     TiffOutputDirectory exifDirectory = null;
@@ -167,13 +172,13 @@
     gpsDirectory.removeField(GpsTagConstants.GPS_TAG_GPS_IMG_DIRECTION_REF);
     gpsDirectory.add(GpsTagConstants.GPS_TAG_GPS_IMG_DIRECTION_REF,
-        GpsTagConstants.GPS_TAG_GPS_IMG_DIRECTION_REF_VALUE_TRUE_NORTH);
+            GpsTagConstants.GPS_TAG_GPS_IMG_DIRECTION_REF_VALUE_TRUE_NORTH);
 
     gpsDirectory.removeField(GpsTagConstants.GPS_TAG_GPS_IMG_DIRECTION);
     gpsDirectory.add(GpsTagConstants.GPS_TAG_GPS_IMG_DIRECTION,
-        RationalNumber.valueOf(image.getCa()));
+            RationalNumber.valueOf(image.getCa()));
 
     exifDirectory.removeField(ExifTagConstants.EXIF_TAG_DATE_TIME_ORIGINAL);
     exifDirectory.add(ExifTagConstants.EXIF_TAG_DATE_TIME_ORIGINAL,
-        ((MapillaryImportedImage) image).getDate("yyyy/MM/dd HH:mm:ss"));
+            ((MapillaryImportedImage) image).getDate("yyyy/MM/dd HH:mm:ss"));
 
     // Removes the ImageDescription tag, that causes problems in the upload.
@@ -197,5 +202,4 @@
    *
    * @param image
-   *
    */
   public static void upload(MapillaryImportedImage image) {
@@ -205,11 +209,10 @@
   /**
    * @param image
-   * @param uuid
-   *          The UUID used to create the sequence.
+   * @param uuid  The UUID used to create the sequence.
    */
   public static void upload(MapillaryImportedImage image, UUID uuid) {
     String key = MapillaryUser.getUsername() + "/" + uuid.toString() + "/"
-        + image.getLatLon().lat() + "_" + image.getLatLon().lon() + "_"
-        + image.getCa() + "_" + image.getCapturedAt() + ".jpg";
+            + image.getLatLon().lat() + "_" + image.getLatLon().lon() + "_"
+            + image.getCa() + "_" + image.getCapturedAt() + ".jpg";
 
     String policy = null;
@@ -236,9 +239,8 @@
    * @param hash
    * @throws IOException
-   * @throws IllegalArgumentException
-   *           if the hash doesn't contain all the needed keys.
+   * @throws IllegalArgumentException if the hash doesn't contain all the needed keys.
    */
   public static void uploadFile(File file, HashMap<String, String> hash)
-      throws IOException {
+          throws IOException {
     HttpClientBuilder builder = HttpClientBuilder.create();
     HttpClient httpClient = builder.build();
@@ -250,5 +252,5 @@
         throw new IllegalArgumentException();
       entityBuilder.addPart(key, new StringBody(hash.get(key),
-          ContentType.TEXT_PLAIN));
+              ContentType.TEXT_PLAIN));
     }
     entityBuilder.addPart("file", new FileBody(file));
@@ -268,12 +270,10 @@
    * Uploads the given {@link MapillarySequence}.
    *
-   * @param sequence
-   *          The sequence to upload. It must contain only
-   *          {@link MapillaryImportedImage} objects.
-   * @param delete
-   *          Whether the images must be deleted after upload or not.
+   * @param sequence The sequence to upload. It must contain only
+   *                 {@link MapillaryImportedImage} objects.
+   * @param delete   Whether the images must be deleted after upload or not.
    */
   public static void uploadSequence(MapillarySequence sequence, boolean delete) {
-    Main.worker.submit(new SequenceUploadThread(sequence.getImages(), delete));
+    Main.worker.submit(new SequenceUploadThread(new ConcurrentSkipListSet(sequence.getImages()), delete));
   }
 }
Index: /applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/utils/MapillaryUtils.java
===================================================================
--- /applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/utils/MapillaryUtils.java	(revision 31881)
+++ /applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/utils/MapillaryUtils.java	(revision 31882)
@@ -11,9 +11,5 @@
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Calendar;
-import java.util.Date;
-import java.util.List;
-import java.util.Locale;
+import java.util.*;
 
 import javax.swing.SwingUtilities;
@@ -40,5 +36,4 @@
  *
  * @author nokutu
- *
  */
 public final class MapillaryUtils {
@@ -97,5 +92,5 @@
    * ({@link java.util.TimeZone#getDefault()}).
    *
-   * @param date The string containing the date.
+   * @param date   The string containing the date.
    * @param format The format of the date.
    * @return The date in Epoch format.
@@ -110,29 +105,26 @@
    * degrees-minutes-seconds-format
    *
-   * @param degMinSec
-   *          an array of length 3, the values in there are (in this order)
-   *          degrees, minutes and seconds
-   * @param ref
-   *          the latitude or longitude reference determining if the given value
-   *          is:
-   *          <ul>
-   *          <li>north (
-   *          {@link GpsTagConstants#GPS_TAG_GPS_LATITUDE_REF_VALUE_NORTH}) or
-   *          south (
-   *          {@link GpsTagConstants#GPS_TAG_GPS_LATITUDE_REF_VALUE_SOUTH}) of
-   *          the equator</li>
-   *          <li>east (
-   *          {@link GpsTagConstants#GPS_TAG_GPS_LONGITUDE_REF_VALUE_EAST}) or
-   *          west ({@link GpsTagConstants#GPS_TAG_GPS_LONGITUDE_REF_VALUE_WEST}
-   *          ) of the equator</li>
-   *          </ul>
+   * @param degMinSec an array of length 3, the values in there are (in this order)
+   *                  degrees, minutes and seconds
+   * @param ref       the latitude or longitude reference determining if the given value
+   *                  is:
+   *                  <ul>
+   *                  <li>north (
+   *                  {@link GpsTagConstants#GPS_TAG_GPS_LATITUDE_REF_VALUE_NORTH}) or
+   *                  south (
+   *                  {@link GpsTagConstants#GPS_TAG_GPS_LATITUDE_REF_VALUE_SOUTH}) of
+   *                  the equator</li>
+   *                  <li>east (
+   *                  {@link GpsTagConstants#GPS_TAG_GPS_LONGITUDE_REF_VALUE_EAST}) or
+   *                  west ({@link GpsTagConstants#GPS_TAG_GPS_LONGITUDE_REF_VALUE_WEST}
+   *                  ) of the equator</li>
+   *                  </ul>
    * @return the decimal degree-value for the given input, negative when west of
-   *         0-meridian or south of equator, positive otherwise
-   * @throws IllegalArgumentException
-   *           if {@code degMinSec} doesn't have length 3 or if {@code ref} is
-   *           not one of the values mentioned above
+   * 0-meridian or south of equator, positive otherwise
+   * @throws IllegalArgumentException if {@code degMinSec} doesn't have length 3 or if {@code ref} is
+   *                                  not one of the values mentioned above
    */
   public static double degMinSecToDouble(RationalNumber[] degMinSec,
-      String ref) {
+                                         String ref) {
     if (degMinSec == null || degMinSec.length != 3) {
       throw new IllegalArgumentException("Array's length must be 3.");
@@ -157,5 +149,5 @@
 
     if (GpsTagConstants.GPS_TAG_GPS_LATITUDE_REF_VALUE_SOUTH.equals(ref)
-        || GpsTagConstants.GPS_TAG_GPS_LONGITUDE_REF_VALUE_WEST.equals(ref)) {
+            || GpsTagConstants.GPS_TAG_GPS_LONGITUDE_REF_VALUE_WEST.equals(ref)) {
       result *= -1;
     }
@@ -168,6 +160,5 @@
    * Returns the extension of a {@link File} object.
    *
-   * @param file
-   *          The {@link File} object whose extension is going to be returned.
+   * @param file The {@link File} object whose extension is going to be returned.
    * @return A {@code String} object containing the extension in lowercase.
    */
@@ -189,6 +180,6 @@
    */
   public static synchronized void join(
-      MapillaryAbstractImage mapillaryAbstractImage,
-      MapillaryAbstractImage mapillaryAbstractImage2) {
+          MapillaryAbstractImage mapillaryAbstractImage,
+          MapillaryAbstractImage mapillaryAbstractImage2) {
     MapillaryAbstractImage firstImage = mapillaryAbstractImage;
     MapillaryAbstractImage secondImage = mapillaryAbstractImage2;
@@ -221,14 +212,11 @@
    * direction) and creates a new icon in that position.
    *
-   * @param file
-   *          The file where the picture is located.
+   * @param file The file where the picture is located.
    * @return The imported image.
-   * @throws ImageReadException
-   *           If the file isn't an image.
-   * @throws IOException
-   *           If the file doesn't have the valid metadata.
+   * @throws ImageReadException If the file isn't an image.
+   * @throws IOException        If the file doesn't have the valid metadata.
    */
   public static MapillaryImportedImage readJPG(File file)
-      throws IOException, ImageReadException {
+          throws IOException, ImageReadException {
     return readJPG(file, false);
   }
@@ -238,42 +226,37 @@
    * direction) and creates a new icon in that position.
    *
-   * @param file
-   *          The {@link File} where the picture is located.
-   * @param exceptionNoTags
-   *          {@code true} if an exception must be thrown if the image doesn't
-   *          have all the needed EXIF tags; {@code false} returns an image in
-   *          the center of the screen.
+   * @param file            The {@link File} where the picture is located.
+   * @param exceptionNoTags {@code true} if an exception must be thrown if the image doesn't
+   *                        have all the needed EXIF tags; {@code false} returns an image in
+   *                        the center of the screen.
    * @return The imported image, whose data has been extracted from the
-   *         picture's metadata.
-   * @throws ImageReadException
-   *           If the {@link File} isn't an image.
-   * @throws IOException
-   *           If the {@link File} doesn't have the valid metadata.
-   * @throws IllegalArgumentException
-   *           if exceptionNoTags is set to {@code true} and the image doesn't
-   *           have the needed EXIF tags.
+   * picture's metadata.
+   * @throws ImageReadException       If the {@link File} isn't an image.
+   * @throws IOException              If the {@link File} doesn't have the valid metadata.
+   * @throws IllegalArgumentException if exceptionNoTags is set to {@code true} and the image doesn't
+   *                                  have the needed EXIF tags.
    */
   public static MapillaryImportedImage readJPG(File file,
-      boolean exceptionNoTags) throws IOException, ImageReadException {
+                                               boolean exceptionNoTags) throws IOException, ImageReadException {
     final ImageMetadata metadata = Imaging.getMetadata(file);
     if (metadata instanceof JpegImageMetadata) {
       final JpegImageMetadata jpegMetadata = (JpegImageMetadata) metadata;
       final TiffField lat_ref = jpegMetadata.findEXIFValueWithExactMatch(
-          GpsTagConstants.GPS_TAG_GPS_LATITUDE_REF);
+              GpsTagConstants.GPS_TAG_GPS_LATITUDE_REF);
       final TiffField lat = jpegMetadata
-          .findEXIFValueWithExactMatch(GpsTagConstants.GPS_TAG_GPS_LATITUDE);
+              .findEXIFValueWithExactMatch(GpsTagConstants.GPS_TAG_GPS_LATITUDE);
       final TiffField lon_ref = jpegMetadata.findEXIFValueWithExactMatch(
-          GpsTagConstants.GPS_TAG_GPS_LONGITUDE_REF);
+              GpsTagConstants.GPS_TAG_GPS_LONGITUDE_REF);
       final TiffField lon = jpegMetadata
-          .findEXIFValueWithExactMatch(GpsTagConstants.GPS_TAG_GPS_LONGITUDE);
+              .findEXIFValueWithExactMatch(GpsTagConstants.GPS_TAG_GPS_LONGITUDE);
       final TiffField ca = jpegMetadata.findEXIFValueWithExactMatch(
-          GpsTagConstants.GPS_TAG_GPS_IMG_DIRECTION);
+              GpsTagConstants.GPS_TAG_GPS_IMG_DIRECTION);
       final TiffField datetimeOriginal = jpegMetadata
-          .findEXIFValueWithExactMatch(
-              ExifTagConstants.EXIF_TAG_DATE_TIME_ORIGINAL);
+              .findEXIFValueWithExactMatch(
+                      ExifTagConstants.EXIF_TAG_DATE_TIME_ORIGINAL);
       if (lat_ref == null || lat == null || lon == null || lon_ref == null) {
         if (exceptionNoTags)
           throw new IllegalArgumentException(
-              "The image doesn't have the needed EXIF tags.");
+                  "The image doesn't have the needed EXIF tags.");
         else
           return readNoTags(file);
@@ -284,13 +267,13 @@
       if (lat.getValue() instanceof RationalNumber[])
         latValue = MapillaryUtils.degMinSecToDouble(
-            (RationalNumber[]) lat.getValue(), lat_ref.getValue().toString());
+                (RationalNumber[]) lat.getValue(), lat_ref.getValue().toString());
       if (lon.getValue() instanceof RationalNumber[])
         lonValue = MapillaryUtils.degMinSecToDouble(
-            (RationalNumber[]) lon.getValue(), lon_ref.getValue().toString());
+                (RationalNumber[]) lon.getValue(), lon_ref.getValue().toString());
       if (ca != null && ca.getValue() instanceof RationalNumber)
         caValue = ((RationalNumber) ca.getValue()).doubleValue();
       if (datetimeOriginal != null)
         return new MapillaryImportedImage(latValue, lonValue, caValue, file,
-            datetimeOriginal.getStringValue());
+                datetimeOriginal.getStringValue());
       else
         return new MapillaryImportedImage(latValue, lonValue, caValue, file);
@@ -303,11 +286,10 @@
    * creates a new icon in the middle of the map.
    *
-   * @param file
-   *          The file where the image is located.
+   * @param file The file where the image is located.
    * @return The imported image.
    */
   public static MapillaryImportedImage readNoTags(File file) {
     return readNoTags(file, Main.map.mapView.getProjection()
-        .eastNorth2latlon(Main.map.mapView.getCenter()));
+            .eastNorth2latlon(Main.map.mapView.getCenter()));
   }
 
@@ -316,9 +298,7 @@
    * creates a new icon in the middle of the map.
    *
-   * @param file
-   *          The file where the image is located.
-   * @param pos
-   *          A {@link LatLon} object indicating the position in the map where
-   *          the image must be set.
+   * @param file The file where the image is located.
+   * @param pos  A {@link LatLon} object indicating the position in the map where
+   *             the image must be set.
    * @return The imported image.
    */
@@ -335,13 +315,13 @@
       final JpegImageMetadata jpegMetadata = (JpegImageMetadata) metadata;
       final TiffField datetimeOriginal = jpegMetadata
-          .findEXIFValueWithExactMatch(
-              ExifTagConstants.EXIF_TAG_DATE_TIME_ORIGINAL);
+              .findEXIFValueWithExactMatch(
+                      ExifTagConstants.EXIF_TAG_DATE_TIME_ORIGINAL);
       if (datetimeOriginal == null)
         return new MapillaryImportedImage(pos.lat(), pos.lon(), 0,
-            file);
+                file);
       else {
         try {
           return new MapillaryImportedImage(pos.lat(), pos.lon(), 0,
-              file, datetimeOriginal.getStringValue());
+                  file, datetimeOriginal.getStringValue());
         } catch (ImageReadException e) {
           Main.error(e);
@@ -355,6 +335,5 @@
    * Reads an image in PNG format.
    *
-   * @param file
-   *          The file where the image is located.
+   * @param file The file where the image is located.
    * @return The imported image.
    */
@@ -374,10 +353,8 @@
    * Zooms to fit all the given {@link MapillaryAbstractImage} objects.
    *
-   * @param images
-   *          The images your are zooming to.
-   * @param select
-   *          Whether the added images must be selected or not.
-   */
-  public static void showPictures(final List<MapillaryAbstractImage> images, final boolean select) {
+   * @param images The images your are zooming to.
+   * @param select Whether the added images must be selected or not.
+   */
+  public static void showPictures(final Set<MapillaryAbstractImage> images, final boolean select) {
     if (!SwingUtilities.isEventDispatchThread()) {
       SwingUtilities.invokeLater(new Runnable() {
@@ -388,13 +365,16 @@
       });
     } else {
-      Bounds zoomBounds;
+      Bounds zoomBounds = null;
       if (images.isEmpty()) {
         zoomBounds = new Bounds(new LatLon(0, 0));
       } else {
-        zoomBounds = new Bounds(images.get(0).getLatLon());
         for (MapillaryAbstractImage img : images) {
-          zoomBounds.extend(img.getLatLon());
+          if (zoomBounds == null) {
+            zoomBounds = new Bounds(img.getLatLon());
+          } else
+            zoomBounds.extend(img.getLatLon());
         }
       }
+
       // The zoom rectangle must have a minimum size.
       double latExtent = Math.max(zoomBounds.getMaxLat() - zoomBounds.getMinLat(), MIN_ZOOM_SQUARE_SIDE);
@@ -409,4 +389,5 @@
         MapillaryData.dataUpdated();
     }
+
   }
 
@@ -418,6 +399,6 @@
    */
   public static synchronized void unjoin(
-      MapillaryAbstractImage mapillaryAbstractImage,
-      MapillaryAbstractImage mapillaryAbstractImage2) {
+          MapillaryAbstractImage mapillaryAbstractImage,
+          MapillaryAbstractImage mapillaryAbstractImage2) {
     MapillaryAbstractImage firstImage = mapillaryAbstractImage;
     MapillaryAbstractImage secondImage = mapillaryAbstractImage2;
@@ -429,10 +410,10 @@
 
     ArrayList<MapillaryAbstractImage> firstHalf = new ArrayList<>(
-        firstImage.getSequence().getImages().subList(0,
-            firstImage.getSequence().getImages().indexOf(secondImage)));
+            firstImage.getSequence().getImages().subList(0,
+                    firstImage.getSequence().getImages().indexOf(secondImage)));
     ArrayList<MapillaryAbstractImage> secondHalf = new ArrayList<>(
-        firstImage.getSequence().getImages().subList(
-            firstImage.getSequence().getImages().indexOf(secondImage),
-            firstImage.getSequence().getImages().size()));
+            firstImage.getSequence().getImages().subList(
+                    firstImage.getSequence().getImages().indexOf(secondImage),
+                    firstImage.getSequence().getImages().size()));
 
     MapillarySequence seq1 = new MapillarySequence();
@@ -457,5 +438,5 @@
     StringBuilder ret = new StringBuilder();
     if (PluginState.isDownloading()) {
-      ret .append(tr("Downloading Mapillary images"));
+      ret.append(tr("Downloading Mapillary images"));
     } else if (MapillaryLayer.getInstance().getData().size() > 0) {
       ret.append(tr("Total Mapillary images: {0}", MapillaryLayer.getInstance().getData().size()));
Index: /applications/editors/josm/plugins/mapillary/test/unit/org/openstreetmap/josm/plugins/mapillary/MapillaryDataTest.java
===================================================================
--- /applications/editors/josm/plugins/mapillary/test/unit/org/openstreetmap/josm/plugins/mapillary/MapillaryDataTest.java	(revision 31881)
+++ /applications/editors/josm/plugins/mapillary/test/unit/org/openstreetmap/josm/plugins/mapillary/MapillaryDataTest.java	(revision 31882)
@@ -5,4 +5,5 @@
 
 import java.util.Arrays;
+import java.util.concurrent.ConcurrentSkipListSet;
 
 import org.junit.Before;
@@ -44,5 +45,5 @@
 
     this.data = new MapillaryData();
-    this.data.add(this.seq.getImages());
+    this.data.add(new ConcurrentSkipListSet(this.seq.getImages()));
   }
 
@@ -59,9 +60,9 @@
     this.data.add(this.img1);
     assertEquals(1, this.data.getImages().size());
-    this.data.add(Arrays.asList(new MapillaryAbstractImage[] { this.img2,
-        this.img3 }));
+    this.data.add(new ConcurrentSkipListSet(Arrays.asList(new MapillaryAbstractImage[] { this.img2,
+        this.img3 })));
     assertEquals(3, this.data.getImages().size());
-    this.data.add(Arrays.asList(new MapillaryAbstractImage[] { this.img3,
-        this.img4 }));
+    this.data.add(new ConcurrentSkipListSet(Arrays.asList(new MapillaryAbstractImage[] { this.img3,
+        this.img4 })));
     assertEquals(4, this.data.getImages().size());
   }
Index: /applications/editors/josm/plugins/mapillary/test/unit/org/openstreetmap/josm/plugins/mapillary/history/MapillaryRecordTest.java
===================================================================
--- /applications/editors/josm/plugins/mapillary/test/unit/org/openstreetmap/josm/plugins/mapillary/history/MapillaryRecordTest.java	(revision 31881)
+++ /applications/editors/josm/plugins/mapillary/test/unit/org/openstreetmap/josm/plugins/mapillary/history/MapillaryRecordTest.java	(revision 31882)
@@ -7,4 +7,5 @@
 
 import java.util.Arrays;
+import java.util.concurrent.ConcurrentSkipListSet;
 
 import org.junit.Before;
@@ -28,5 +29,4 @@
  *
  * @author nokutu
- *
  */
 public class MapillaryRecordTest extends AbstractTest {
@@ -56,17 +56,17 @@
   public void commandTest() {
     MapillaryCommand cmd12 = new CommandMove(
-        Arrays.asList(new MapillaryAbstractImage[] { this.img1, this.img2 }),
-        0.1, 0.1);
+            new ConcurrentSkipListSet(Arrays.asList(new MapillaryAbstractImage[]{this.img1, this.img2})),
+            0.1, 0.1);
     MapillaryCommand cmd23 = new CommandMove(
-        Arrays.asList(new MapillaryAbstractImage[] { this.img2, this.img3 }),
-        0.1, 0.1);
+            new ConcurrentSkipListSet(Arrays.asList(new MapillaryAbstractImage[]{this.img2, this.img3})),
+            0.1, 0.1);
     MapillaryCommand cmd13 = new CommandMove(
-        Arrays.asList(new MapillaryAbstractImage[] { this.img1, this.img3 }),
-        0.1, 0.1);
+            new ConcurrentSkipListSet(Arrays.asList(new MapillaryAbstractImage[]{this.img1, this.img3})),
+            0.1, 0.1);
     MapillaryCommand cmd1 = new CommandMove(
-        Arrays.asList(new MapillaryAbstractImage[] { this.img1 }), 0.1, 0.1);
+            new ConcurrentSkipListSet(Arrays.asList(new MapillaryAbstractImage[]{this.img1})), 0.1, 0.1);
     MapillaryCommand cmd31 = new CommandMove(
-        Arrays.asList(new MapillaryAbstractImage[] { this.img3, this.img1 }),
-        0.2, 0.2);
+            new ConcurrentSkipListSet(Arrays.asList(new MapillaryAbstractImage[]{this.img3, this.img1})),
+            0.2, 0.2);
     this.record.addCommand(cmd12);
     this.record.addCommand(cmd23);
@@ -112,9 +112,9 @@
   public void commandMoveTest() {
     CommandMove cmd1 = new CommandMove(
-        Arrays.asList(new MapillaryAbstractImage[] { this.img1, this.img2 }),
-        0.1, 0.1);
+            new ConcurrentSkipListSet(Arrays.asList(new MapillaryAbstractImage[]{this.img1, this.img2})),
+            0.1, 0.1);
     CommandMove cmd2 = new CommandMove(
-        Arrays.asList(new MapillaryAbstractImage[] { this.img1, this.img2 }),
-        0.1, 0.1);
+            new ConcurrentSkipListSet(Arrays.asList(new MapillaryAbstractImage[]{this.img1, this.img2})),
+            0.1, 0.1);
 
     this.record.addCommand(cmd1);
@@ -146,9 +146,9 @@
   public void commandTurnTest() {
     CommandTurn cmd1 = new CommandTurn(
-        Arrays.asList(new MapillaryAbstractImage[] { this.img1, this.img2 }),
-        0.2);
+            new ConcurrentSkipListSet(Arrays.asList(new MapillaryAbstractImage[]{this.img1, this.img2})),
+            0.2);
     CommandTurn cmd2 = new CommandTurn(
-        Arrays.asList(new MapillaryAbstractImage[] { this.img1, this.img2 }),
-        0.1);
+            new ConcurrentSkipListSet(Arrays.asList(new MapillaryAbstractImage[]{this.img1, this.img2})),
+            0.1);
 
     this.record.addCommand(cmd1);
@@ -177,7 +177,7 @@
   public void commandJoinClass() {
     CommandJoin cmd1 = new CommandJoin(
-        Arrays.asList(new MapillaryAbstractImage[] { this.img1, this.img2 }));
+            Arrays.asList(new MapillaryAbstractImage[]{this.img1, this.img2}));
     CommandJoin cmd2 = new CommandJoin(
-        Arrays.asList(new MapillaryAbstractImage[] { this.img2, this.img3 }));
+            Arrays.asList(new MapillaryAbstractImage[]{this.img2, this.img3}));
 
     this.record.addCommand(cmd1);
@@ -193,6 +193,6 @@
     try {
       this.record.addCommand(new CommandJoin(Arrays
-          .asList(new MapillaryAbstractImage[] { this.img1, this.img2,
-              this.img3 })));
+              .asList(new MapillaryAbstractImage[]{this.img1, this.img2,
+                      this.img3})));
       fail();
     } catch (IllegalArgumentException e) {
@@ -209,12 +209,12 @@
   public void commandUnjoinClass() {
     CommandJoin join1 = new CommandJoin(
-        Arrays.asList(new MapillaryAbstractImage[] { this.img1, this.img2 }));
+            Arrays.asList(new MapillaryAbstractImage[]{this.img1, this.img2}));
     CommandJoin join2 = new CommandJoin(
-        Arrays.asList(new MapillaryAbstractImage[] { this.img2, this.img3 }));
+            Arrays.asList(new MapillaryAbstractImage[]{this.img2, this.img3}));
 
     CommandUnjoin cmd1 = new CommandUnjoin(
-        Arrays.asList(new MapillaryAbstractImage[] { this.img1, this.img2 }));
+            Arrays.asList(new MapillaryAbstractImage[]{this.img1, this.img2}));
     CommandUnjoin cmd2 = new CommandUnjoin(
-        Arrays.asList(new MapillaryAbstractImage[] { this.img2, this.img3 }));
+            Arrays.asList(new MapillaryAbstractImage[]{this.img2, this.img3}));
 
     this.record.addCommand(join1);
@@ -232,6 +232,6 @@
     try {
       this.record.addCommand(new CommandUnjoin(Arrays
-          .asList(new MapillaryAbstractImage[] { this.img1, this.img2,
-              this.img3 })));
+              .asList(new MapillaryAbstractImage[]{this.img1, this.img2,
+                      this.img3})));
       fail();
     } catch (IllegalArgumentException e) {
@@ -248,12 +248,12 @@
   public void commandDeleteTest() {
     CommandJoin join1 = new CommandJoin(
-        Arrays.asList(new MapillaryAbstractImage[] { this.img1, this.img2 }));
+            Arrays.asList(new MapillaryAbstractImage[]{this.img1, this.img2}));
     CommandJoin join2 = new CommandJoin(
-        Arrays.asList(new MapillaryAbstractImage[] { this.img2, this.img3 }));
+            Arrays.asList(new MapillaryAbstractImage[]{this.img2, this.img3}));
 
     CommandDelete cmd1 = new CommandDelete(
-        Arrays.asList(new MapillaryAbstractImage[] { this.img1 }));
+            new ConcurrentSkipListSet(Arrays.asList(new MapillaryAbstractImage[]{this.img1})));
     CommandDelete cmd2 = new CommandDelete(
-        Arrays.asList(new MapillaryAbstractImage[] { this.img2, this.img3 }));
+            new ConcurrentSkipListSet(Arrays.asList(new MapillaryAbstractImage[]{this.img2, this.img3})));
 
     this.record.addCommand(join1);
@@ -261,9 +261,9 @@
 
     MapillaryLayer
-        .getInstance()
-        .getData()
-        .add(
-            Arrays.asList(new MapillaryAbstractImage[] { this.img1, this.img2,
-                this.img3 }));
+            .getInstance()
+            .getData()
+            .add(
+                    new ConcurrentSkipListSet(Arrays.asList(new MapillaryAbstractImage[]{this.img1, this.img2,
+                            this.img3})));
 
     this.record.addCommand(cmd1);
@@ -286,7 +286,7 @@
 
     CommandImport cmd1 = new CommandImport(
-        Arrays.asList(new MapillaryAbstractImage[] { this.img1 }));
+            new ConcurrentSkipListSet(Arrays.asList(new MapillaryAbstractImage[]{this.img1})));
     CommandImport cmd2 = new CommandImport(
-        Arrays.asList(new MapillaryAbstractImage[] { this.img2, this.img3 }));
+            new ConcurrentSkipListSet(Arrays.asList(new MapillaryAbstractImage[]{this.img2, this.img3})));
 
     this.record.addCommand(cmd1);
