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 31512)
+++ applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/MapillaryAbstractImage.java	(revision 31513)
@@ -1,8 +1,7 @@
 package org.openstreetmap.josm.plugins.mapillary;
 
-import java.util.Date;
-import java.text.ParseException;
 import java.text.SimpleDateFormat;
 import java.util.Calendar;
+import java.util.Date;
 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReentrantLock;
@@ -25,5 +24,5 @@
    * images to prevent concurrency problems.
    */
-  public static Lock LOCK = new ReentrantLock();
+  public static final Lock LOCK = new ReentrantLock();
 
   /** The time the image was captured, in Epoch format. */
@@ -35,6 +34,4 @@
   /** Direction of the picture. */
   public final double ca;
-  /** If the image has been modified from its initial values. */
-  public boolean isModified = false;
   /** Temporal position of the picture until it is uploaded. */
   public LatLon tempLatLon;
@@ -51,8 +48,9 @@
    */
   protected double movingCa;
+  /** Whether the image must be drown in the map or not */
   private boolean visible;
 
   /**
-   * Main constructor of the class.
+   * Creates a new object in the given position and with the given direction.
    *
    * @param lat
@@ -74,85 +72,4 @@
 
   /**
-   * Returns whether the object has been modified or not.
-   *
-   * @return true if the object has been modified; false otherwise.
-   */
-  public boolean isModified() {
-    return this.isModified;
-  }
-
-  /**
-   * Returns a LatLon object containing the current coordinates of the object.
-   * When you are dragging the image this changes.
-   *
-   * @return The LatLon object with the position of the object.
-   */
-  public LatLon getLatLon() {
-    return this.movingLatLon;
-  }
-
-  /**
-   * Returns whether the image is visible on the map or not.
-   *
-   * @return True if the image is visible; false otherwise.
-   */
-  public boolean isVisible() {
-    return this.visible;
-  }
-
-  /**
-   * Set's whether the image should be visible on the map or not.
-   *
-   * @param visible
-   *          true if the image is set to be visible; false otherwise.
-   */
-  public void setVisible(boolean visible) {
-    this.visible = visible;
-  }
-
-  /**
-   * Returns the last fixed coordinates of the object.
-   *
-   * @return A LatLon object containing.
-   */
-  public LatLon getTempLatLon() {
-    return this.tempLatLon;
-  }
-
-  /**
-   * Moves the image temporally to another position
-   *
-   * @param x
-   *          The movement of the image in longitude units.
-   * @param y
-   *          The movement of the image in latitude units.
-   */
-  public void move(double x, double y) {
-    this.movingLatLon = new LatLon(this.tempLatLon.getY() + y,
-        this.tempLatLon.getX() + x);
-    this.isModified = true;
-  }
-
-  /**
-   * Turns the image direction.
-   *
-   * @param ca
-   *          The angle the image is moving.
-   */
-  public void turn(double ca) {
-    this.movingCa = this.tempCa + ca;
-    this.isModified = true;
-  }
-
-  /**
-   * Called when the mouse button is released, meaning that the picture has
-   * stopped being dragged.
-   */
-  public void stopMoving() {
-    this.tempLatLon = this.movingLatLon;
-    this.tempCa = this.movingCa;
-  }
-
-  /**
    * Returns the direction towards the image has been taken.
    *
@@ -164,10 +81,10 @@
 
   /**
-   * Returns the last fixed direction of the object.
-   *
-   * @return The last fixed direction of the object. 0 means north.
-   */
-  public double getTempCa() {
-    return this.tempCa;
+   * Returns the Epoch time when the image was captured.
+   *
+   * @return The long containing the Epoch time when the image was captured.
+   */
+  public long getCapturedAt() {
+    return this.capturedAt;
   }
 
@@ -193,23 +110,4 @@
 
   /**
-   * Sets the Epoch time when the picture was captured.
-   *
-   * @param capturedAt
-   *          Epoch time when the image was captured.
-   */
-  public void setCapturedAt(long capturedAt) {
-    this.capturedAt = capturedAt;
-  }
-
-  /**
-   * Returns the Epoch time when the image was captured.
-   *
-   * @return The long containing the Epoch time when the image was captured.
-   */
-  public long getCapturedAt() {
-    return this.capturedAt;
-  }
-
-  /**
    * Returns the date the picture was taken in the given format.
    *
@@ -228,39 +126,11 @@
 
   /**
-   * Parses a string with a given format and returns the Epoch time.
-   *
-   * @param date
-   *          The string containing the date.
-   * @param format
-   *          The format of the date.
-   * @return The date in Epoch format.
-   * @throws ParseException
-   */
-  public static long getEpoch(String date, String format) throws ParseException {
-
-    SimpleDateFormat formatter = new SimpleDateFormat(format);
-    Date dateTime = formatter.parse(date);
-    return dateTime.getTime();
-
-  }
-
-  /**
-   * Returns current time in Epoch format
-   *
-   * @return The current date in Epoch format.
-   */
-  protected static long currentTime() {
-    Calendar cal = Calendar.getInstance();
-    return cal.getTimeInMillis();
-  }
-
-  /**
-   * Sets the MapillarySequence object which contains the MapillaryImage.
-   *
-   * @param sequence
-   *          The MapillarySequence that contains the MapillaryImage.
-   */
-  public void setSequence(MapillarySequence sequence) {
-    this.sequence = sequence;
+   * Returns a LatLon object containing the current coordinates of the object.
+   * When you are dragging the image this changes.
+   *
+   * @return The LatLon object with the position of the object.
+   */
+  public LatLon getLatLon() {
+    return this.movingLatLon;
   }
 
@@ -277,4 +147,53 @@
 
     return this.sequence;
+  }
+
+  /**
+   * Returns the last fixed direction of the object.
+   *
+   * @return The last fixed direction of the object. 0 means north.
+   */
+  public double getTempCa() {
+    return this.tempCa;
+  }
+
+  /**
+   * Returns the last fixed coordinates of the object.
+   *
+   * @return A LatLon object containing.
+   */
+  public LatLon getTempLatLon() {
+    return this.tempLatLon;
+  }
+
+  /**
+   * Returns whether the object has been modified or not.
+   *
+   * @return true if the object has been modified; false otherwise.
+   */
+  public boolean isModified() {
+    return (this.getLatLon() != this.latLon || this.getCa() != this.ca);
+  }
+
+  /**
+   * Returns whether the image is visible on the map or not.
+   *
+   * @return True if the image is visible; false otherwise.
+   */
+  public boolean isVisible() {
+    return this.visible;
+  }
+
+  /**
+   * Moves the image temporally to another position
+   *
+   * @param x
+   *          The movement of the image in longitude units.
+   * @param y
+   *          The movement of the image in latitude units.
+   */
+  public void move(double x, double y) {
+    this.movingLatLon = new LatLon(this.tempLatLon.getY() + y,
+        this.tempLatLon.getX() + x);
   }
 
@@ -315,3 +234,52 @@
 
   }
+
+  /**
+   * Sets the Epoch time when the picture was captured.
+   *
+   * @param capturedAt
+   *          Epoch time when the image was captured.
+   */
+  public void setCapturedAt(long capturedAt) {
+    this.capturedAt = capturedAt;
+  }
+
+  /**
+   * Sets the MapillarySequence object which contains the MapillaryImage.
+   *
+   * @param sequence
+   *          The MapillarySequence that contains the MapillaryImage.
+   */
+  public void setSequence(MapillarySequence sequence) {
+    this.sequence = sequence;
+  }
+
+  /**
+   * Set's whether the image should be visible on the map or not.
+   *
+   * @param visible
+   *          true if the image is set to be visible; false otherwise.
+   */
+  public void setVisible(boolean visible) {
+    this.visible = visible;
+  }
+
+  /**
+   * Called when the mouse button is released, meaning that the picture has
+   * stopped being dragged, so the temporal values are saved.
+   */
+  public void stopMoving() {
+    this.tempLatLon = this.movingLatLon;
+    this.tempCa = this.movingCa;
+  }
+
+  /**
+   * Turns the image direction.
+   *
+   * @param ca
+   *          The angle the image is moving.
+   */
+  public void turn(double ca) {
+    this.movingCa = this.tempCa + ca;
+  }
 }
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 31512)
+++ applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/MapillaryData.java	(revision 31513)
@@ -1,3 +1,7 @@
 package org.openstreetmap.josm.plugins.mapillary;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
 
 import org.openstreetmap.josm.Main;
@@ -5,8 +9,4 @@
 import org.openstreetmap.josm.plugins.mapillary.cache.CacheUtils;
 import org.openstreetmap.josm.plugins.mapillary.gui.MapillaryMainDialog;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.CopyOnWriteArrayList;
 
 /**
@@ -33,5 +33,5 @@
 
   /**
-   * Main constructor.
+   * Creates a new object and adds the initial set of listeners.
    */
   protected MapillaryData() {
@@ -93,6 +93,6 @@
    *
    * @param images
-   *          A {@link List} of {@link MapillaryAbstractImage} objects that are going
-   *          to be removed.
+   *          A {@link List} of {@link MapillaryAbstractImage} objects that are
+   *          going to be removed.
    */
   public synchronized void remove(List<MapillaryAbstractImage> images) {
@@ -347,6 +347,6 @@
 
   /**
-   * Adds a {@link MapillaryImage} object to the list of selected images, (when ctrl +
-   * click)
+   * Adds a {@link MapillaryImage} object to the list of selected images, (when
+   * ctrl + click)
    *
    * @param image
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 31512)
+++ applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/MapillaryImportedImage.java	(revision 31513)
@@ -59,11 +59,11 @@
     this.file = file;
     try {
-      this.datetimeOriginal = getEpoch(datetimeOriginal, "yyyy:MM:dd hh:mm:ss");
+      this.datetimeOriginal = MapillaryUtils.getEpoch(datetimeOriginal, "yyyy:MM:dd hh:mm:ss");
     } catch (ParseException e) {
       try {
-        this.datetimeOriginal = getEpoch(datetimeOriginal,
+        this.datetimeOriginal = MapillaryUtils.getEpoch(datetimeOriginal,
             "yyyy/MM/dd hh:mm:ss");
       } catch (ParseException e1) {
-        this.datetimeOriginal = currentTime();
+        this.datetimeOriginal = MapillaryUtils.currentTime();
       }
     }
Index: applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/MapillaryLayer.java
===================================================================
--- applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/MapillaryLayer.java	(revision 31512)
+++ applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/MapillaryLayer.java	(revision 31513)
@@ -1,40 +1,6 @@
 package org.openstreetmap.josm.plugins.mapillary;
 
+import static org.openstreetmap.josm.tools.I18n.marktr;
 import static org.openstreetmap.josm.tools.I18n.tr;
-import static org.openstreetmap.josm.tools.I18n.marktr;
-
-import org.openstreetmap.josm.plugins.mapillary.cache.CacheUtils;
-import org.openstreetmap.josm.plugins.mapillary.downloads.MapillaryDownloader;
-import org.openstreetmap.josm.plugins.mapillary.gui.MapillaryFilterDialog;
-import org.openstreetmap.josm.plugins.mapillary.gui.MapillaryMainDialog;
-import org.openstreetmap.josm.plugins.mapillary.history.MapillaryRecord;
-import org.openstreetmap.josm.plugins.mapillary.history.commands.CommandDelete;
-import org.openstreetmap.josm.plugins.mapillary.mode.AbstractMode;
-import org.openstreetmap.josm.plugins.mapillary.mode.JoinMode;
-import org.openstreetmap.josm.plugins.mapillary.mode.SelectMode;
-import org.openstreetmap.josm.plugins.mapillary.utils.MapillaryUtils;
-import org.openstreetmap.josm.Main;
-import org.openstreetmap.josm.gui.layer.Layer;
-import org.openstreetmap.josm.data.Bounds;
-import org.openstreetmap.josm.gui.MapView;
-import org.openstreetmap.josm.gui.MapView.EditLayerChangeListener;
-import org.openstreetmap.josm.gui.MapView.LayerChangeListener;
-import org.openstreetmap.josm.gui.NavigatableComponent;
-import org.openstreetmap.josm.gui.layer.AbstractModifiableLayer;
-import org.openstreetmap.josm.gui.layer.OsmDataLayer;
-import org.openstreetmap.josm.data.coor.LatLon;
-import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
-import org.openstreetmap.josm.data.osm.visitor.paint.PaintColors;
-import org.openstreetmap.josm.gui.dialogs.LayerListDialog;
-import org.openstreetmap.josm.gui.dialogs.LayerListPopup;
-import org.openstreetmap.josm.data.osm.event.PrimitivesAddedEvent;
-import org.openstreetmap.josm.data.osm.event.PrimitivesRemovedEvent;
-import org.openstreetmap.josm.data.osm.event.TagsChangedEvent;
-import org.openstreetmap.josm.data.osm.event.NodeMovedEvent;
-import org.openstreetmap.josm.data.osm.event.WayNodesChangedEvent;
-import org.openstreetmap.josm.data.osm.event.RelationMembersChangedEvent;
-import org.openstreetmap.josm.data.osm.event.AbstractDatasetChangedEvent;
-import org.openstreetmap.josm.data.osm.event.DataChangedEvent;
-import org.openstreetmap.josm.data.osm.event.DataSetListener;
 
 import java.awt.AlphaComposite;
@@ -51,15 +17,48 @@
 import java.awt.image.AffineTransformOp;
 import java.awt.image.BufferedImage;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
 
 import javax.swing.AbstractAction;
-import javax.swing.ImageIcon;
 import javax.swing.Action;
 import javax.swing.Icon;
+import javax.swing.ImageIcon;
 import javax.swing.JComponent;
 import javax.swing.KeyStroke;
 
-import java.util.List;
-import java.util.ArrayList;
-import java.util.concurrent.CopyOnWriteArrayList;
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.Bounds;
+import org.openstreetmap.josm.data.coor.LatLon;
+import org.openstreetmap.josm.data.osm.event.AbstractDatasetChangedEvent;
+import org.openstreetmap.josm.data.osm.event.DataChangedEvent;
+import org.openstreetmap.josm.data.osm.event.DataSetListener;
+import org.openstreetmap.josm.data.osm.event.NodeMovedEvent;
+import org.openstreetmap.josm.data.osm.event.PrimitivesAddedEvent;
+import org.openstreetmap.josm.data.osm.event.PrimitivesRemovedEvent;
+import org.openstreetmap.josm.data.osm.event.RelationMembersChangedEvent;
+import org.openstreetmap.josm.data.osm.event.TagsChangedEvent;
+import org.openstreetmap.josm.data.osm.event.WayNodesChangedEvent;
+import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
+import org.openstreetmap.josm.data.osm.visitor.paint.PaintColors;
+import org.openstreetmap.josm.gui.MapView;
+import org.openstreetmap.josm.gui.MapView.EditLayerChangeListener;
+import org.openstreetmap.josm.gui.MapView.LayerChangeListener;
+import org.openstreetmap.josm.gui.NavigatableComponent;
+import org.openstreetmap.josm.gui.dialogs.LayerListDialog;
+import org.openstreetmap.josm.gui.dialogs.LayerListPopup;
+import org.openstreetmap.josm.gui.layer.AbstractModifiableLayer;
+import org.openstreetmap.josm.gui.layer.Layer;
+import org.openstreetmap.josm.gui.layer.OsmDataLayer;
+import org.openstreetmap.josm.plugins.mapillary.cache.CacheUtils;
+import org.openstreetmap.josm.plugins.mapillary.downloads.MapillaryDownloader;
+import org.openstreetmap.josm.plugins.mapillary.gui.MapillaryFilterDialog;
+import org.openstreetmap.josm.plugins.mapillary.gui.MapillaryMainDialog;
+import org.openstreetmap.josm.plugins.mapillary.history.MapillaryRecord;
+import org.openstreetmap.josm.plugins.mapillary.history.commands.CommandDelete;
+import org.openstreetmap.josm.plugins.mapillary.mode.AbstractMode;
+import org.openstreetmap.josm.plugins.mapillary.mode.JoinMode;
+import org.openstreetmap.josm.plugins.mapillary.mode.SelectMode;
+import org.openstreetmap.josm.plugins.mapillary.utils.MapillaryUtils;
 
 /**
@@ -515,5 +514,54 @@
     // When more data is downloaded, a delayed update is thrown, in order to
     // wait for the data bounds to be set.
-    Main.worker.submit(new delayedDownload());
+    Main.worker.submit(new DelayedDownload());
+  }
+
+  @Override
+  public void primitivesAdded(PrimitivesAddedEvent event) {
+  }
+
+  @Override
+  public void primitivesRemoved(PrimitivesRemovedEvent event) {
+  }
+
+  @Override
+  public void tagsChanged(TagsChangedEvent event) {
+  }
+
+  @Override
+  public void nodeMoved(NodeMovedEvent event) {
+  }
+
+  @Override
+  public void wayNodesChanged(WayNodesChangedEvent event) {
+  }
+
+  @Override
+  public void relationMembersChanged(RelationMembersChangedEvent event) {
+  }
+
+  @Override
+  public void otherDatasetChange(AbstractDatasetChangedEvent event) {
+  }
+
+  @Override
+  public void visitBoundingBox(BoundingXYVisitor v) {
+  }
+
+  @Override
+  public void activeLayerChange(Layer oldLayer, Layer newLayer) {
+    if (newLayer == this) {
+      MapillaryUtils.updateHelpText();
+      MapillaryPlugin.setMenuEnabled(MapillaryPlugin.JOIN_MENU, true);
+    } else
+      MapillaryPlugin.setMenuEnabled(MapillaryPlugin.JOIN_MENU, false);
+  }
+
+  @Override
+  public void layerAdded(Layer newLayer) {
+  }
+
+  @Override
+  public void layerRemoved(Layer oldLayer) {
   }
 
@@ -524,5 +572,5 @@
    *
    */
-  private class delayedDownload extends Thread {
+  private class DelayedDownload extends Thread {
 
     @Override
@@ -537,53 +585,4 @@
   }
 
-  @Override
-  public void primitivesAdded(PrimitivesAddedEvent event) {
-  }
-
-  @Override
-  public void primitivesRemoved(PrimitivesRemovedEvent event) {
-  }
-
-  @Override
-  public void tagsChanged(TagsChangedEvent event) {
-  }
-
-  @Override
-  public void nodeMoved(NodeMovedEvent event) {
-  }
-
-  @Override
-  public void wayNodesChanged(WayNodesChangedEvent event) {
-  }
-
-  @Override
-  public void relationMembersChanged(RelationMembersChangedEvent event) {
-  }
-
-  @Override
-  public void otherDatasetChange(AbstractDatasetChangedEvent event) {
-  }
-
-  @Override
-  public void visitBoundingBox(BoundingXYVisitor v) {
-  }
-
-  @Override
-  public void activeLayerChange(Layer oldLayer, Layer newLayer) {
-    if (newLayer == this) {
-      MapillaryUtils.updateHelpText();
-      MapillaryPlugin.setMenuEnabled(MapillaryPlugin.JOIN_MENU, true);
-    } else
-      MapillaryPlugin.setMenuEnabled(MapillaryPlugin.JOIN_MENU, false);
-  }
-
-  @Override
-  public void layerAdded(Layer newLayer) {
-  }
-
-  @Override
-  public void layerRemoved(Layer oldLayer) {
-  }
-
   /**
    * Action used to delete images.
Index: applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/MapillaryPlugin.java
===================================================================
--- applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/MapillaryPlugin.java	(revision 31512)
+++ applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/MapillaryPlugin.java	(revision 31513)
@@ -8,19 +8,27 @@
 
 import org.apache.commons.jcs.access.CacheAccess;
+import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.data.cache.BufferedImageCacheEntry;
 import org.openstreetmap.josm.data.cache.JCSCacheManager;
 import org.openstreetmap.josm.gui.MainMenu;
-import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.gui.MapFrame;
 import org.openstreetmap.josm.gui.preferences.PreferenceSetting;
 import org.openstreetmap.josm.plugins.Plugin;
 import org.openstreetmap.josm.plugins.PluginInformation;
+import org.openstreetmap.josm.plugins.mapillary.actions.MapillaryDownloadAction;
+import org.openstreetmap.josm.plugins.mapillary.actions.MapillaryDownloadViewAction;
+import org.openstreetmap.josm.plugins.mapillary.actions.MapillaryExportAction;
+import org.openstreetmap.josm.plugins.mapillary.actions.MapillaryImportAction;
+import org.openstreetmap.josm.plugins.mapillary.actions.MapillaryImportIntoSequenceAction;
+import org.openstreetmap.josm.plugins.mapillary.actions.MapillaryJoinAction;
+import org.openstreetmap.josm.plugins.mapillary.actions.MapillaryUploadAction;
+import org.openstreetmap.josm.plugins.mapillary.actions.MapillaryWalkAction;
+import org.openstreetmap.josm.plugins.mapillary.actions.MapillaryZoomAction;
 import org.openstreetmap.josm.plugins.mapillary.downloads.MapillaryDownloader;
 import org.openstreetmap.josm.plugins.mapillary.gui.MapillaryFilterDialog;
 import org.openstreetmap.josm.plugins.mapillary.gui.MapillaryHistoryDialog;
+import org.openstreetmap.josm.plugins.mapillary.gui.MapillaryMainDialog;
 import org.openstreetmap.josm.plugins.mapillary.gui.MapillaryPreferenceSetting;
-import org.openstreetmap.josm.plugins.mapillary.gui.MapillaryMainDialog;
 import org.openstreetmap.josm.plugins.mapillary.oauth.MapillaryUser;
-import org.openstreetmap.josm.plugins.mapillary.actions.*;
 import org.openstreetmap.josm.tools.ImageProvider;
 
Index: applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/downloads/MapillarySequenceDownloadThread.java
===================================================================
--- applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/downloads/MapillarySequenceDownloadThread.java	(revision 31512)
+++ applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/downloads/MapillarySequenceDownloadThread.java	(revision 31513)
@@ -3,11 +3,6 @@
 import java.io.BufferedReader;
 import java.io.IOException;
+import java.io.InputStreamReader;
 import java.net.URL;
-import java.io.InputStreamReader;
-
-import javax.json.JsonArray;
-import javax.json.JsonObject;
-import javax.json.Json;
-
 import java.util.ArrayList;
 import java.util.List;
@@ -15,4 +10,8 @@
 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReentrantLock;
+
+import javax.json.Json;
+import javax.json.JsonArray;
+import javax.json.JsonObject;
 
 import org.openstreetmap.josm.Main;
Index: applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/downloads/MapillarySquareDownloadManagerThread.java
===================================================================
--- applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/downloads/MapillarySquareDownloadManagerThread.java	(revision 31512)
+++ applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/downloads/MapillarySquareDownloadManagerThread.java	(revision 31513)
@@ -1,6 +1,4 @@
 package org.openstreetmap.josm.plugins.mapillary.downloads;
 
-import java.util.concurrent.ThreadPoolExecutor;
-import java.util.concurrent.TimeUnit;
 import java.io.UnsupportedEncodingException;
 import java.net.URLEncoder;
@@ -8,4 +6,6 @@
 import java.util.concurrent.ArrayBlockingQueue;
 import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
 
 import org.openstreetmap.josm.Main;
Index: applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/gui/HyperlinkLabel.java
===================================================================
--- applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/gui/HyperlinkLabel.java	(revision 31512)
+++ applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/gui/HyperlinkLabel.java	(revision 31513)
@@ -125,5 +125,5 @@
       add(this.copyTag);
 
-      this.edit = new JMenuItem(tr("Edit on webpage"));
+      this.edit = new JMenuItem(tr("Edit on website"));
       this.edit.addActionListener(new editAction());
       add(this.edit);
Index: applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/gui/MapillaryHistoryDialog.java
===================================================================
--- applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/gui/MapillaryHistoryDialog.java	(revision 31512)
+++ applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/gui/MapillaryHistoryDialog.java	(revision 31513)
@@ -23,4 +23,5 @@
 import javax.swing.event.TreeSelectionEvent;
 import javax.swing.event.TreeSelectionListener;
+import javax.swing.tree.DefaultMutableTreeNode;
 import javax.swing.tree.DefaultTreeCellRenderer;
 import javax.swing.tree.DefaultTreeModel;
@@ -37,6 +38,4 @@
 import org.openstreetmap.josm.tools.ImageProvider;
 import org.openstreetmap.josm.tools.Shortcut;
-
-import javax.swing.tree.DefaultMutableTreeNode;
 
 /**
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 31512)
+++ applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/gui/MapillaryMainDialog.java	(revision 31513)
@@ -3,7 +3,4 @@
 import static org.openstreetmap.josm.tools.I18n.tr;
 
-import java.awt.event.ActionEvent;
-import java.awt.event.KeyEvent;
-import java.awt.image.BufferedImage;
 import java.awt.BorderLayout;
 import java.awt.Color;
@@ -11,4 +8,7 @@
 import java.awt.FlowLayout;
 import java.awt.GridLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.KeyEvent;
+import java.awt.image.BufferedImage;
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
@@ -16,10 +16,17 @@
 import java.util.List;
 
+import javax.imageio.ImageIO;
+import javax.swing.AbstractAction;
+import javax.swing.JComponent;
+import javax.swing.JPanel;
+import javax.swing.KeyStroke;
+import javax.swing.SwingUtilities;
+
 import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.data.cache.CacheEntry;
 import org.openstreetmap.josm.data.cache.CacheEntryAttributes;
 import org.openstreetmap.josm.data.cache.ICachedLoaderListener;
+import org.openstreetmap.josm.gui.SideButton;
 import org.openstreetmap.josm.gui.dialogs.ToggleDialog;
-import org.openstreetmap.josm.gui.SideButton;
 import org.openstreetmap.josm.plugins.mapillary.MapillaryAbstractImage;
 import org.openstreetmap.josm.plugins.mapillary.MapillaryDataListener;
@@ -33,11 +40,4 @@
 import org.openstreetmap.josm.tools.ImageProvider;
 import org.openstreetmap.josm.tools.Shortcut;
-
-import javax.imageio.ImageIO;
-import javax.swing.JComponent;
-import javax.swing.KeyStroke;
-import javax.swing.SwingUtilities;
-import javax.swing.AbstractAction;
-import javax.swing.JPanel;
 
 /**
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 31512)
+++ applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/history/commands/CommandMove.java	(revision 31513)
@@ -41,5 +41,4 @@
       image.stopMoving();
     }
-    checkModified();
     if (Main.main != null)
       Main.map.repaint();
@@ -52,5 +51,4 @@
       image.stopMoving();
     }
-    checkModified();
     if (Main.main != null)
       Main.map.repaint();
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 31512)
+++ applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/history/commands/CommandTurn.java	(revision 31513)
@@ -36,5 +36,4 @@
       image.stopMoving();
     }
-    checkModified();
     if (Main.main != null)
       Main.map.repaint();
@@ -47,5 +46,4 @@
       image.stopMoving();
     }
-    checkModified();
     if (Main.main != null)
       Main.map.repaint();
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 31512)
+++ applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/history/commands/MapillaryCommand.java	(revision 31513)
@@ -45,12 +45,4 @@
   public abstract void sum(MapillaryCommand command);
 
-  /**
-   * Checks if the image has been modified, comparing with its original values.
-   */
-  public void checkModified() {
-    for (MapillaryAbstractImage image : this.images)
-      image.isModified = (image.tempLatLon == image.latLon || image.tempCa == image.ca);
-  }
-
   @Override
   public abstract String toString();
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 31512)
+++ applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/oauth/UploadUtils.java	(revision 31513)
@@ -55,100 +55,4 @@
 public class UploadUtils {
 
-  /** 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. */
-  private static int c = 0;
-
-  /**
-   * Uploads the given MapillaryImportedImage object.
-   *
-   * @param image
-   *
-   */
-  public static void upload(MapillaryImportedImage image) {
-    upload(image, UUID.randomUUID());
-
-  }
-
-  /**
-   * @param image
-   * @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.datetimeOriginal + ".jpg";
-
-    String policy = null;
-    String signature = null;
-    policy = MapillaryUser.getSecrets().get("images_policy");
-    signature = MapillaryUser.getSecrets().get("images_hash");
-
-    HashMap<String, String> hash = new HashMap<>();
-    hash.put("key", key);
-    hash.put("AWSAccessKeyId", "AKIAI2X3BJAT2W75HILA");
-    hash.put("acl", "private");
-    hash.put("policy", policy);
-    hash.put("signature", signature);
-    hash.put("Content-Type", "image/jpeg");
-
-    try {
-      uploadFile(updateFile(image), hash);
-    } catch (ImageReadException | ImageWriteException | IOException e) {
-      Main.error(e);
-    }
-  }
-
-  /**
-   * @param file
-   * @param hash
-   * @throws IOException
-   * @throws IllegalArgumentException
-   *           if the hash doesn't contain all the needed keys.
-   */
-  public static void uploadFile(File file, HashMap<String, String> hash)
-      throws IOException {
-    HttpClientBuilder builder = HttpClientBuilder.create();
-    HttpClient httpClient = builder.build();
-    HttpPost httpPost = new HttpPost(UPLOAD_URL);
-
-    MultipartEntityBuilder entityBuilder = MultipartEntityBuilder.create();
-    for (String key : keys) {
-      if (hash.get(key) == null)
-        throw new IllegalArgumentException();
-      entityBuilder.addPart(key, new StringBody(hash.get(key),
-          ContentType.TEXT_PLAIN));
-    }
-    entityBuilder.addPart("file", new FileBody(file));
-
-    HttpEntity entity = entityBuilder.build();
-    httpPost.setEntity(entity);
-    HttpResponse response = httpClient.execute(httpPost);
-    if (response.getStatusLine().toString().contains("204")) {
-      PluginState.imageUploaded();
-      Main.info(PluginState.getUploadString() + " (Mapillary)");
-    } else
-      Main.info("Upload error");
-    file.delete();
-    MapillaryUtils.updateHelpText();
-  }
-
-  /**
-   * 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.
-   */
-  public static void uploadSequence(MapillarySequence sequence, boolean delete) {
-    Main.worker.submit(new SequenceUploadThread(sequence.getImages(), delete));
-  }
-
   private static class SequenceUploadThread extends Thread {
     private List<MapillaryAbstractImage> images;
@@ -194,5 +98,4 @@
     }
   }
-
   private static class SingleUploadThread extends Thread {
 
@@ -210,4 +113,13 @@
     }
   }
+  /** 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. */
+  private static int c = 0;
 
   /**
@@ -282,3 +194,91 @@
     return tempFile;
   }
+
+  /**
+   * Uploads the given MapillaryImportedImage object.
+   *
+   * @param image
+   *
+   */
+  public static void upload(MapillaryImportedImage image) {
+    upload(image, UUID.randomUUID());
+
+  }
+
+  /**
+   * @param image
+   * @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.datetimeOriginal + ".jpg";
+
+    String policy = null;
+    String signature = null;
+    policy = MapillaryUser.getSecrets().get("images_policy");
+    signature = MapillaryUser.getSecrets().get("images_hash");
+
+    HashMap<String, String> hash = new HashMap<>();
+    hash.put("key", key);
+    hash.put("AWSAccessKeyId", "AKIAI2X3BJAT2W75HILA");
+    hash.put("acl", "private");
+    hash.put("policy", policy);
+    hash.put("signature", signature);
+    hash.put("Content-Type", "image/jpeg");
+
+    try {
+      uploadFile(updateFile(image), hash);
+    } catch (ImageReadException | ImageWriteException | IOException e) {
+      Main.error(e);
+    }
+  }
+
+  /**
+   * @param file
+   * @param hash
+   * @throws IOException
+   * @throws IllegalArgumentException
+   *           if the hash doesn't contain all the needed keys.
+   */
+  public static void uploadFile(File file, HashMap<String, String> hash)
+      throws IOException {
+    HttpClientBuilder builder = HttpClientBuilder.create();
+    HttpClient httpClient = builder.build();
+    HttpPost httpPost = new HttpPost(UPLOAD_URL);
+
+    MultipartEntityBuilder entityBuilder = MultipartEntityBuilder.create();
+    for (String key : keys) {
+      if (hash.get(key) == null)
+        throw new IllegalArgumentException();
+      entityBuilder.addPart(key, new StringBody(hash.get(key),
+          ContentType.TEXT_PLAIN));
+    }
+    entityBuilder.addPart("file", new FileBody(file));
+
+    HttpEntity entity = entityBuilder.build();
+    httpPost.setEntity(entity);
+    HttpResponse response = httpClient.execute(httpPost);
+    if (response.getStatusLine().toString().contains("204")) {
+      PluginState.imageUploaded();
+      Main.info(PluginState.getUploadString() + " (Mapillary)");
+    } else
+      Main.info("Upload error");
+    file.delete();
+    MapillaryUtils.updateHelpText();
+  }
+
+  /**
+   * 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.
+   */
+  public static void uploadSequence(MapillarySequence sequence, boolean delete) {
+    Main.worker.submit(new SequenceUploadThread(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 31512)
+++ applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/utils/MapillaryUtils.java	(revision 31513)
@@ -8,7 +8,9 @@
 import java.net.URISyntaxException;
 import java.net.URL;
+import java.text.ParseException;
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Calendar;
+import java.util.Date;
 import java.util.List;
 
@@ -45,20 +47,46 @@
 
   /**
-   * Updates the help text at the bottom of the window.
-   */
-  public static void updateHelpText() {
-    String ret = "";
-    if (PluginState.isDownloading())
-      ret += tr("Downloading Mapillary images");
-    else if (MapillaryLayer.getInstance().getData().size() > 0)
-      ret += tr("Total Mapillary images: {0}", MapillaryLayer.getInstance()
-          .getData().size());
-    else
-      ret += tr("No images found");
-    if (MapillaryLayer.getInstance().mode != null)
-      ret += " -- " + tr(MapillaryLayer.getInstance().mode.toString());
-    if (PluginState.isUploading())
-      ret += " -- " + PluginState.getUploadString();
-    Main.map.statusLine.setHelpText(ret);
+   * Open the default browser in the given URL.
+   *
+   * @param url
+   *          The URL that is going to be opened.
+   */
+  public static void browse(URL url) {
+    Desktop desktop = Desktop.getDesktop();
+    if (desktop.isSupported(Desktop.Action.BROWSE)) {
+      try {
+        desktop.browse(url.toURI());
+      } catch (IOException | URISyntaxException e1) {
+        Main.error(e1);
+      }
+    } else {
+      Runtime runtime = Runtime.getRuntime();
+      try {
+        runtime.exec("xdg-open " + url);
+      } catch (IOException exc) {
+        exc.printStackTrace();
+      }
+    }
+  }
+
+  /**
+   * Returns the current date.
+   *
+   * @return A {@code String} object containing the current date.
+   */
+  public static String currentDate() {
+    Calendar cal = Calendar.getInstance();
+    SimpleDateFormat formatter = new SimpleDateFormat("yyyy:MM:dd hh:mm:ss");
+    return formatter.format(cal.getTime());
+  }
+
+  /**
+   * Returns current time in Epoch format
+   *
+   * @return The current date in Epoch format.
+   */
+  public static long currentTime() {
+    Calendar cal = Calendar.getInstance();
+    return cal.getTimeInMillis();
   }
 
@@ -122,25 +150,34 @@
 
   /**
-   * Open the default browser in the given URL.
-   *
-   * @param url
-   *          The URL that is going to be opened.
-   */
-  public static void browse(URL url) {
-    Desktop desktop = Desktop.getDesktop();
-    if (desktop.isSupported(Desktop.Action.BROWSE)) {
-      try {
-        desktop.browse(url.toURI());
-      } catch (IOException | URISyntaxException e1) {
-        Main.error(e1);
-      }
-    } else {
-      Runtime runtime = Runtime.getRuntime();
-      try {
-        runtime.exec("xdg-open " + url);
-      } catch (IOException exc) {
-        exc.printStackTrace();
-      }
-    }
+   * Parses a string with a given format and returns the Epoch time.
+   *
+   * @param date
+   *          The string containing the date.
+   * @param format
+   *          The format of the date.
+   * @return The date in Epoch format.
+   * @throws ParseException
+   */
+  public static long getEpoch(String date, String format) throws ParseException {
+    SimpleDateFormat formatter = new SimpleDateFormat(format);
+    Date dateTime = formatter.parse(date);
+    return dateTime.getTime();
+  }
+
+  /**
+   * Returns the extension of a {@link File} object.
+   *
+   * @param file
+   *          The {@link File} object whose extension is going to be returned.
+   * @return A {@code String} object containing the extension in lowercase.
+   */
+  public static String getExtension(File file) {
+    if (file.isDirectory())
+      throw new IllegalArgumentException("The file is a directory");
+    int k = file.getName().lastIndexOf('.');
+    if (k > 0) {
+      return file.getName().substring(k + 1).toLowerCase();
+    }
+    throw new IllegalArgumentException("Error parsing the extension");
   }
 
@@ -178,116 +215,4 @@
     if (Main.main != null)
       MapillaryData.dataUpdated();
-  }
-
-  /**
-   * Separates two images belonging to the same sequence.
-   *
-   * @param mapillaryAbstractImage
-   * @param mapillaryAbstractImage2
-   */
-  public synchronized static void unjoin(
-      MapillaryAbstractImage mapillaryAbstractImage,
-      MapillaryAbstractImage mapillaryAbstractImage2) {
-    MapillaryAbstractImage firstImage = mapillaryAbstractImage;
-    MapillaryAbstractImage secondImage = mapillaryAbstractImage2;
-
-    if (mapillaryAbstractImage.next() != mapillaryAbstractImage2) {
-      firstImage = mapillaryAbstractImage2;
-      secondImage = mapillaryAbstractImage;
-    }
-
-    ArrayList<MapillaryAbstractImage> firstHalf = new ArrayList<>(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()));
-
-    MapillarySequence seq1 = new MapillarySequence();
-    MapillarySequence seq2 = new MapillarySequence();
-
-    for (MapillaryAbstractImage img : firstHalf) {
-      img.setSequence(seq1);
-      seq1.add(img);
-    }
-    for (MapillaryAbstractImage img : secondHalf) {
-      img.setSequence(seq2);
-      seq2.add(img);
-    }
-    if (Main.main != null)
-      MapillaryData.dataUpdated();
-  }
-
-  /**
-   * 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) {
-    if (!SwingUtilities.isEventDispatchThread()) {
-      SwingUtilities.invokeLater(new Runnable() {
-        @Override
-        public void run() {
-          showPictures(images, select);
-        }
-      });
-    } else {
-      double minLat = 90;
-      double minLon = 180;
-      double maxLat = -90;
-      double maxLon = -180;
-      for (MapillaryAbstractImage img : images) {
-        if (img.getLatLon().lat() < minLat)
-          minLat = img.getLatLon().lat();
-        if (img.getLatLon().lon() < minLon)
-          minLon = img.getLatLon().lon();
-        if (img.getLatLon().lat() > maxLat)
-          maxLat = img.getLatLon().lat();
-        if (img.getLatLon().lon() > maxLon)
-          maxLon = img.getLatLon().lon();
-      }
-      Bounds zoomBounds = new Bounds(new LatLon(minLat, minLon), new LatLon(
-          maxLat, maxLon));
-      // The zoom rectangle must have a minimum size.
-      double latExtent = zoomBounds.getMaxLat() - zoomBounds.getMinLat() >= MIN_ZOOM_SQUARE_SIDE ? zoomBounds
-          .getMaxLat() - zoomBounds.getMinLat()
-          : MIN_ZOOM_SQUARE_SIDE;
-      double lonExtent = zoomBounds.getMaxLon() - zoomBounds.getMinLon() >= MIN_ZOOM_SQUARE_SIDE ? zoomBounds
-          .getMaxLon() - zoomBounds.getMinLon()
-          : MIN_ZOOM_SQUARE_SIDE;
-      zoomBounds = new Bounds(zoomBounds.getCenter(), latExtent, lonExtent);
-
-      Main.map.mapView.zoomTo(zoomBounds);
-      MapillaryLayer.getInstance().getData().setSelectedImage(null);
-      if (select)
-        MapillaryLayer.getInstance().getData().addMultiSelectedImage(images);
-      if (Main.main != null)
-        MapillaryData.dataUpdated();
-    }
-  }
-
-  /**
-   * Zooms to fit all the {@link MapillaryAbstractImage} objects stored in the
-   * database.
-   */
-  public static void showAllPictures() {
-    showPictures(MapillaryLayer.getInstance().getData().getImages(), false);
-  }
-
-  /**
-   * Returns the current date.
-   *
-   * @return A {@code String} object containing the current date.
-   */
-  public static String currentDate() {
-    Calendar cal = Calendar.getInstance();
-    SimpleDateFormat formatter = new SimpleDateFormat("yyyy:MM:dd hh:mm:ss");
-    return formatter.format(cal.getTime());
   }
 
@@ -319,5 +244,6 @@
    *          have all the needed EXIF tags; {@code false} returns an image in
    *          the center of the screen.
-   * @return The imported image.
+   * @return The imported image, whose data has been extracted from the
+   *         picture's metadata.
    * @throws ImageReadException
    *           If the {@link File} isn't an image.
@@ -378,4 +304,19 @@
    * @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()));
+  }
+
+  /**
+   * Reads a image file that doesn't contain the needed GPS information. And
+   * 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
@@ -406,33 +347,123 @@
 
   /**
-   * Reads a image file that doesn't contain the needed GPS information. And
-   * creates a new icon in the middle of the map.
-   *
-   * @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()));
-  }
-
-  /**
-   * Returns the extension of a {@link File} object.
-   *
-   * @param file
-   *          The {@link File} object whose extension is going to be returned.
-   * @return A {@code String} object containing the extension.
-   */
-  public static String getExtension(File file) {
-    if (file.isDirectory())
-      throw new IllegalArgumentException("The file is a directory");
-    int k = file.getName().lastIndexOf('.');
-    if (k > 0) {
-      return file.getName().substring(k + 1).toLowerCase();
-    }
-    throw new IllegalArgumentException("Error parsing the extension");
+   * Zooms to fit all the {@link MapillaryAbstractImage} objects stored in the
+   * database.
+   */
+  public static void showAllPictures() {
+    showPictures(MapillaryLayer.getInstance().getData().getImages(), false);
+  }
+
+  /**
+   * 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) {
+    if (!SwingUtilities.isEventDispatchThread()) {
+      SwingUtilities.invokeLater(new Runnable() {
+        @Override
+        public void run() {
+          showPictures(images, select);
+        }
+      });
+    } else {
+      double minLat = 90;
+      double minLon = 180;
+      double maxLat = -90;
+      double maxLon = -180;
+      for (MapillaryAbstractImage img : images) {
+        if (img.getLatLon().lat() < minLat)
+          minLat = img.getLatLon().lat();
+        if (img.getLatLon().lon() < minLon)
+          minLon = img.getLatLon().lon();
+        if (img.getLatLon().lat() > maxLat)
+          maxLat = img.getLatLon().lat();
+        if (img.getLatLon().lon() > maxLon)
+          maxLon = img.getLatLon().lon();
+      }
+      Bounds zoomBounds = new Bounds(new LatLon(minLat, minLon), new LatLon(
+          maxLat, maxLon));
+      // The zoom rectangle must have a minimum size.
+      double latExtent = zoomBounds.getMaxLat() - zoomBounds.getMinLat() >= MIN_ZOOM_SQUARE_SIDE ? zoomBounds
+          .getMaxLat() - zoomBounds.getMinLat()
+          : MIN_ZOOM_SQUARE_SIDE;
+      double lonExtent = zoomBounds.getMaxLon() - zoomBounds.getMinLon() >= MIN_ZOOM_SQUARE_SIDE ? zoomBounds
+          .getMaxLon() - zoomBounds.getMinLon()
+          : MIN_ZOOM_SQUARE_SIDE;
+      zoomBounds = new Bounds(zoomBounds.getCenter(), latExtent, lonExtent);
+
+      Main.map.mapView.zoomTo(zoomBounds);
+      MapillaryLayer.getInstance().getData().setSelectedImage(null);
+      if (select)
+        MapillaryLayer.getInstance().getData().addMultiSelectedImage(images);
+      if (Main.main != null)
+        MapillaryData.dataUpdated();
+    }
+  }
+
+  /**
+   * Separates two images belonging to the same sequence.
+   *
+   * @param mapillaryAbstractImage
+   * @param mapillaryAbstractImage2
+   */
+  public synchronized static void unjoin(
+      MapillaryAbstractImage mapillaryAbstractImage,
+      MapillaryAbstractImage mapillaryAbstractImage2) {
+    MapillaryAbstractImage firstImage = mapillaryAbstractImage;
+    MapillaryAbstractImage secondImage = mapillaryAbstractImage2;
+
+    if (mapillaryAbstractImage.next() != mapillaryAbstractImage2) {
+      firstImage = mapillaryAbstractImage2;
+      secondImage = mapillaryAbstractImage;
+    }
+
+    ArrayList<MapillaryAbstractImage> firstHalf = new ArrayList<>(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()));
+
+    MapillarySequence seq1 = new MapillarySequence();
+    MapillarySequence seq2 = new MapillarySequence();
+
+    for (MapillaryAbstractImage img : firstHalf) {
+      img.setSequence(seq1);
+      seq1.add(img);
+    }
+    for (MapillaryAbstractImage img : secondHalf) {
+      img.setSequence(seq2);
+      seq2.add(img);
+    }
+    if (Main.main != null)
+      MapillaryData.dataUpdated();
+  }
+
+  /**
+   * Updates the help text at the bottom of the window.
+   */
+  public static void updateHelpText() {
+    String ret = "";
+    if (PluginState.isDownloading())
+      ret += tr("Downloading Mapillary images");
+    else if (MapillaryLayer.getInstance().getData().size() > 0)
+      ret += tr("Total Mapillary images: {0}", MapillaryLayer.getInstance()
+          .getData().size());
+    else
+      ret += tr("No images found");
+    if (MapillaryLayer.getInstance().mode != null)
+      ret += " -- " + tr(MapillaryLayer.getInstance().mode.toString());
+    if (PluginState.isUploading())
+      ret += " -- " + PluginState.getUploadString();
+    synchronized (MapillaryUtils.class) {
+      Main.map.statusLine.setHelpText(ret);
+    }
   }
 }
