Index: /applications/editors/josm/plugins/MicrosoftStreetside/.classpath
===================================================================
--- /applications/editors/josm/plugins/MicrosoftStreetside/.classpath	(revision 34328)
+++ /applications/editors/josm/plugins/MicrosoftStreetside/.classpath	(revision 34329)
@@ -1,45 +1,36 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <classpath>
-	<!--  -classpathentry including="data/**|images/**|LICENSE|LICENSE_*" kind="src" path=""/>
-	<classpathentry kind="src" path="src"/>
-	<classpathentry including="**/*.po" kind="src" path="poSrc"/>
-	<classpathentry kind="src" path="test/data"/>
-	<classpathentry kind="src" path="test/unit"/>
+	<classpathentry including="data/**|images/**|LICENSE|LICENSE_*" kind="src" output="bin" path="">
+		<attributes>
+			<attribute name="gradle_scope" value="main"/>
+			<attribute name="gradle_used_by_scope" value="main,test"/>
+		</attributes>
+	</classpathentry>
+	<classpathentry kind="src" output="bin" path="src">
+		<attributes>
+			<attribute name="gradle_scope" value="minJosmVersion"/>
+			<attribute name="gradle_used_by_scope" value="minJosmVersion"/>
+		</attributes>
+	</classpathentry>
+	<classpathentry kind="src" output="bin" path="test/unit">
+		<attributes>
+			<attribute name="gradle_scope" value="test"/>
+			<attribute name="gradle_used_by_scope" value="test"/>
+		</attributes>
+	</classpathentry>
+	<classpathentry including="**/*.po" kind="src" output="bin" path="poSrc">
+		<attributes>
+			<attribute name="gradle_scope" value="main"/>
+			<attribute name="gradle_used_by_scope" value="main,test"/>
+		</attributes>
+	</classpathentry>
+	<classpathentry kind="src" output="bin" path="test/data">
+		<attributes>
+			<attribute name="gradle_scope" value="test"/>
+			<attribute name="gradle_used_by_scope" value="test"/>
+		</attributes>
+	</classpathentry>
 	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8/"/>
 	<classpathentry kind="con" path="org.eclipse.buildship.core.gradleclasspathcontainer"/>
-	<classpathentry kind="output" path="bin"/ -->
-	
-	<classpathentry including="data/**|images/**|LICENSE|LICENSE_*" kind="src" output="bin/main" path="">
-        <attributes>
-            <attribute name="gradle_scope" value="main"/>
-            <attribute name="gradle_used_by_scope" value="main,test"/>
-        </attributes>
-    </classpathentry>
-    <classpathentry kind="src" output="bin/minJosmVersion" path="src">
-        <attributes>
-            <attribute name="gradle_scope" value="minJosmVersion"/>
-            <attribute name="gradle_used_by_scope" value="minJosmVersion"/>
-        </attributes>
-    </classpathentry>
-    <classpathentry kind="src" output="bin/test" path="test/unit">
-        <attributes>
-            <attribute name="gradle_scope" value="test"/>
-            <attribute name="gradle_used_by_scope" value="test"/>
-        </attributes>
-    </classpathentry>
-    <classpathentry including="**/*.po" kind="src" output="bin/main" path="poSrc">
-        <attributes>
-            <attribute name="gradle_scope" value="main"/>
-            <attribute name="gradle_used_by_scope" value="main,test"/>
-        </attributes>
-    </classpathentry>
-    <classpathentry kind="src" output="bin/test" path="test/data">
-        <attributes>
-            <attribute name="gradle_scope" value="test"/>
-            <attribute name="gradle_used_by_scope" value="test"/>
-        </attributes>
-    </classpathentry>
-    <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8/"/>
-    <classpathentry kind="con" path="org.eclipse.buildship.core.gradleclasspathcontainer"/>
-    <classpathentry kind="output" path="bin/default"/>
+	<classpathentry kind="output" path="bin/default"/>
 </classpath>
Index: /applications/editors/josm/plugins/MicrosoftStreetside/build.gradle
===================================================================
--- /applications/editors/josm/plugins/MicrosoftStreetside/build.gradle	(revision 34328)
+++ /applications/editors/josm/plugins/MicrosoftStreetside/build.gradle	(revision 34329)
@@ -2,11 +2,10 @@
   id "org.sonarqube" version "2.6.2"
   id "org.kordamp.markdown.convert" version "1.1.0"
-  id 'org.openstreetmap.josm' version "0.5.0"
+  id 'org.openstreetmap.josm' version "0.4.4"
   id "com.github.ben-manes.versions" version "0.17.0"
-  id 'com.github.spotbugs' version '1.6.2'
+  id 'com.github.spotbugs' version '1.6.1'
   id "net.ltgt.errorprone" version "0.0.14"
 
   id 'eclipse'
-  id 'idea'
   id 'jacoco'
   id 'java'
@@ -41,4 +40,5 @@
   compile 'us.monoid.web:resty:0.3.2'
   testImplementation ('org.openstreetmap.josm:josm-unittest'){changing=true}
+  testImplementation 'com.github.tomakehurst:wiremock:2.17.0'
   testImplementation 'junit:junit:4.12'
 }
@@ -73,7 +73,14 @@
   debugPort = 7051
   manifest {
+    // See https://floscher.github.io/gradle-josm-plugin/kdoc/current/gradle-josm-plugin/org.openstreetmap.josm.gradle.plugin.config/-josm-manifest/old-version-download-link.html
+    //oldVersionDownloadLink 13643, 'v1.5.14', new URL("https://github.com/JOSM/Mapillary/releases/download/v1.5.14/Mapillary.jar")
+    //oldVersionDownloadLink 13558, 'v1.5.12+pre13643', new URL('https://github.com/JOSM/Mapillary/releases/download/v1.5.12%2Bpre13643/Mapillary.jar')
+    //oldVersionDownloadLink 12987, 'v1.5.10', new URL('https://github.com/JOSM/Mapillary/releases/download/v1.5.10/Mapillary.jar')
+    //oldVersionDownloadLink 12675, 'v1.5.7', new URL('https://github.com/JOSM/Mapillary/releases/download/v1.5.7/Mapillary.jar')
+    //oldVersionDownloadLink 12128, 'v1.5.5', new URL('https://github.com/JOSM/Mapillary/releases/download/v1.5.5/Mapillary.jar')
+    //oldVersionDownloadLink 10824, 'v1.5.3', new URL('https://github.com/JOSM/Mapillary/releases/download/v1.5.3/Mapillary.jar')
   }
   i18n {
-    //pathTransformer = getGithubPathTransformer('JOSM/Mapillary')
+    pathTransformer = getGithubPathTransformer('spatialdev/MicrosoftStreetside')
   }
 }
@@ -121,3 +128,2 @@
   }
 }
-
Index: /applications/editors/josm/plugins/MicrosoftStreetside/build.xml
===================================================================
--- /applications/editors/josm/plugins/MicrosoftStreetside/build.xml	(revision 34328)
+++ /applications/editors/josm/plugins/MicrosoftStreetside/build.xml	(revision 34329)
@@ -14,9 +14,9 @@
 	<property name="plugin.main.version" value="13860" />
 	
-    <property name="plugin.author" value="renerr18" />
+  <property name="plugin.author" value="renerr18" />
 	<property name="plugin.class" value="org.openstreetmap.josm.plugins.streetside.StreetsidePlugin" />
 	<property name="plugin.description" value="View high resolution Microsoft Streetside 360 degree imagery in JOSM." />
 	<property name="plugin.icon" value="images/streetside-logo-white.png" />
-	<!--property name="plugin.link" value="https://github.com/JOSM/MicrosoftStreetside"/-->
+	<property name="plugin.link" value="https://github.com/spatialdev/MicrosoftStreetside"/>
 	<property name="plugin.requires" value="apache-commons;apache-http;"/>
 
@@ -34,7 +34,4 @@
 	<path id="ivy.lib.path" path="ant/ivy-2.4.0.jar" />
 	<taskdef resource="org/apache/ivy/ant/antlib.xml" uri="antlib:org.apache.ivy.ant" classpathref="ivy.lib.path" />
-
-	<!-- ** include targets that all plugins have in common ** -->
-	<import file="../build-common.xml" />
 
 	<fileset id="plugin.requires.jars" dir="${plugin.dist.dir}">
@@ -62,5 +59,5 @@
 
 	<target name="install-plugin" depends="clean, dist, install">
-		<echo>Installed MicrosoftStreetside plugin</echo>
+		<echo>Installed Microsoft Streetside plugin</echo>
 	</target>
 
Index: /applications/editors/josm/plugins/MicrosoftStreetside/config/codecov/codecov.yml
===================================================================
--- /applications/editors/josm/plugins/MicrosoftStreetside/config/codecov/codecov.yml	(revision 34328)
+++ /applications/editors/josm/plugins/MicrosoftStreetside/config/codecov/codecov.yml	(revision 34329)
@@ -9,4 +9,4 @@
   model_and_api:
     paths:
-      - src/org/openstreetmap/josm/plugins/ms-streetside-josm-plugin/utils/api/
-      - src/org/openstreetmap/josm/plugins/ms-streetside-josm-plugin/model/
+      - src/org/openstreetmap/josm/plugins/streetside/utils/api/
+      - src/org/openstreetmap/josm/plugins/streetside/model/
Index: /applications/editors/josm/plugins/MicrosoftStreetside/config/pmd/ruleset.xml
===================================================================
--- /applications/editors/josm/plugins/MicrosoftStreetside/config/pmd/ruleset.xml	(revision 34328)
+++ /applications/editors/josm/plugins/MicrosoftStreetside/config/pmd/ruleset.xml	(revision 34329)
@@ -1,4 +1,4 @@
 <?xml version="1.0"?>
-<ruleset name="Ruleset for jms-streetside-josm-plugin"
+<ruleset name="Ruleset for ms-streetside-josm-plugin"
     xmlns="http://pmd.sourceforge.net/ruleset/2.0.0"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
Index: /applications/editors/josm/plugins/MicrosoftStreetside/gradle/tool-config.gradle
===================================================================
--- /applications/editors/josm/plugins/MicrosoftStreetside/gradle/tool-config.gradle	(revision 34328)
+++ /applications/editors/josm/plugins/MicrosoftStreetside/gradle/tool-config.gradle	(revision 34329)
@@ -1,6 +1,6 @@
-def pmdVersion = "5.8.1"
-def spotbugsVersion = "3.1.1"
-def jacocoVersion = "0.7.9"
-def errorproneVersion = "2.2.0"
+def pmdVersion = "5.8.0" // TODO: Update to PMD 6
+def spotbugsVersion = "3.1.3"
+def jacocoVersion = "0.8.1"
+def errorproneVersion = "2.3.1"
 
 // Set up ErrorProne (currently only for JDK8, until JDK9 is supported)
Index: /applications/editors/josm/plugins/MicrosoftStreetside/gradle/wrapper/gradle-wrapper.properties
===================================================================
--- /applications/editors/josm/plugins/MicrosoftStreetside/gradle/wrapper/gradle-wrapper.properties	(revision 34328)
+++ /applications/editors/josm/plugins/MicrosoftStreetside/gradle/wrapper/gradle-wrapper.properties	(revision 34329)
@@ -1,5 +1,5 @@
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-4.8-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-4.7-all.zip
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
Index: /applications/editors/josm/plugins/MicrosoftStreetside/ivy.xml
===================================================================
--- /applications/editors/josm/plugins/MicrosoftStreetside/ivy.xml	(revision 34328)
+++ /applications/editors/josm/plugins/MicrosoftStreetside/ivy.xml	(revision 34329)
@@ -1,7 +1,6 @@
 <ivy-module version="2.0">
 	<info organisation="org.openstreetmap.josm.plugins" module="MicrosoftStreetside" revision="0.0.1" />
-	<!--  configurations defaultconf="default" defaultconfmapping="default->default,sources,javadoc"-->
-	<configurations defaultconf="default" defaultconfmapping="default->default">
-		<conf name="default" />
+	<configurations defaultconf="default" defaultconfmapping="default->default,sources">
+			<conf name="default" />
 	</configurations>
 	<dependencies>
Index: /applications/editors/josm/plugins/MicrosoftStreetside/src/org/openstreetmap/josm/plugins/streetside/StreetsideData.java
===================================================================
--- /applications/editors/josm/plugins/MicrosoftStreetside/src/org/openstreetmap/josm/plugins/streetside/StreetsideData.java	(revision 34328)
+++ /applications/editors/josm/plugins/MicrosoftStreetside/src/org/openstreetmap/josm/plugins/streetside/StreetsideData.java	(revision 34329)
@@ -59,6 +59,6 @@
    */
   protected StreetsideData() {
-    selectedImage = null;
-    bounds = new CopyOnWriteArrayList<>();
+    this.selectedImage = null;
+    this.bounds = new CopyOnWriteArrayList<>();
 
   // Adds the basic set of listeners.
@@ -135,9 +135,9 @@
    */
   public void addMultiSelectedImage(final StreetsideAbstractImage image) {
-    if (!multiSelectedImages.contains(image)) {
-      if (getSelectedImage() == null) {
+    if (!this.multiSelectedImages.contains(image)) {
+      if (this.getSelectedImage() == null) {
         this.setSelectedImage(image);
       } else {
-        multiSelectedImages.add(image);
+        this.multiSelectedImages.add(image);
       }
     }
@@ -152,9 +152,9 @@
    */
   public void addMultiSelectedImage(Collection<StreetsideAbstractImage> images) {
-    images.stream().filter(image -> !multiSelectedImages.contains(image)).forEach(image -> {
-      if (getSelectedImage() == null) {
+    images.stream().filter(image -> !this.multiSelectedImages.contains(image)).forEach(image -> {
+      if (this.getSelectedImage() == null) {
         this.setSelectedImage(image);
       } else {
-        multiSelectedImages.add(image);
+        this.multiSelectedImages.add(image);
       }
     });
@@ -190,5 +190,5 @@
    */
   public StreetsideAbstractImage getHighlightedImage() {
-    return highlightedImage;
+    return this.highlightedImage;
   }
 
@@ -317,15 +317,15 @@
    */
   public void setSelectedImage(StreetsideAbstractImage image, boolean zoom) {
-    StreetsideAbstractImage oldImage = selectedImage;
-    selectedImage = image;
-    multiSelectedImages.clear();
+    StreetsideAbstractImage oldImage = this.selectedImage;
+    this.selectedImage = image;
+    this.multiSelectedImages.clear();
     final MapView mv = StreetsidePlugin.getMapView();
     if (image != null) {
-      multiSelectedImages.add(image);
+      this.multiSelectedImages.add(image);
       if (mv != null && image instanceof StreetsideImage) {
         StreetsideImage streetsideImage = (StreetsideImage) image;
 
         // Downloading thumbnails of surrounding pictures.
-        StreetsideData.downloadSurroundingImages(streetsideImage);
+        downloadSurroundingImages(streetsideImage);
       }
     }
@@ -333,5 +333,5 @@
       mv.zoomTo(selectedImage.getMovingLatLon());
     }
-    fireSelectedImageChanged(oldImage, selectedImage);
+    fireSelectedImageChanged(oldImage, this.selectedImage);
     StreetsideLayer.invalidateInstance();
   }
@@ -379,5 +379,5 @@
    */
   public Set<StreetsideAbstractImage> getMultiSelectedImages() {
-    return multiSelectedImages;
+    return this.multiSelectedImages;
   }
 
Index: /applications/editors/josm/plugins/MicrosoftStreetside/src/org/openstreetmap/josm/plugins/streetside/StreetsideLayer.java
===================================================================
--- /applications/editors/josm/plugins/MicrosoftStreetside/src/org/openstreetmap/josm/plugins/streetside/StreetsideLayer.java	(revision 34328)
+++ /applications/editors/josm/plugins/MicrosoftStreetside/src/org/openstreetmap/josm/plugins/streetside/StreetsideLayer.java	(revision 34329)
@@ -12,5 +12,7 @@
 import java.awt.RenderingHints;
 import java.awt.TexturePaint;
+import java.awt.event.ActionEvent;
 import java.awt.geom.Line2D;
+import java.awt.geom.Path2D;
 import java.awt.image.BufferedImage;
 import java.util.Comparator;
@@ -18,6 +20,9 @@
 import java.util.Optional;
 
+import javax.swing.AbstractAction;
 import javax.swing.Action;
 import javax.swing.Icon;
+import javax.swing.JComponent;
+import javax.swing.KeyStroke;
 
 import org.openstreetmap.josm.Main;
@@ -34,10 +39,9 @@
 import org.openstreetmap.josm.gui.layer.AbstractModifiableLayer;
 import org.openstreetmap.josm.gui.layer.Layer;
-import org.openstreetmap.josm.gui.layer.LayerManager;
 import org.openstreetmap.josm.gui.layer.MainLayerManager.ActiveLayerChangeEvent;
 import org.openstreetmap.josm.gui.layer.MainLayerManager.ActiveLayerChangeListener;
 import org.openstreetmap.josm.plugins.streetside.cache.CacheUtils;
+import org.openstreetmap.josm.plugins.streetside.gui.StreetsideChangesetDialog;
 import org.openstreetmap.josm.plugins.streetside.gui.StreetsideMainDialog;
-import org.openstreetmap.josm.plugins.streetside.history.StreetsideRecord;
 import org.openstreetmap.josm.plugins.streetside.io.download.StreetsideDownloader;
 import org.openstreetmap.josm.plugins.streetside.io.download.StreetsideDownloader.DOWNLOAD_MODE;
@@ -51,4 +55,7 @@
 import org.openstreetmap.josm.tools.I18n;
 import org.openstreetmap.josm.tools.ImageProvider.ImageSizes;
+import org.openstreetmap.josm.tools.Logging;
+
+import org.openstreetmap.josm.plugins.streetside.history.StreetsideRecord;
 
 /**
@@ -61,16 +68,16 @@
 ActiveLayerChangeListener, StreetsideDataListener {
 
-	/** The radius of the image marker */
-	private static final int IMG_MARKER_RADIUS = 7;
-	/** The radius of the circular sector that indicates the camera angle */
-	private static final int CA_INDICATOR_RADIUS = 15;
-	/** The angle of the circular sector that indicates the camera angle */
-	private static final int CA_INDICATOR_ANGLE = 40;
-	/** Length of the edge of the small sign, which indicates that traffic signs have been found in an image. */
-	private static final int TRAFFIC_SIGN_SIZE = 6;
-	/** A third of the height of the sign, for easier calculations */
-	private static final double TRAFFIC_SIGN_HEIGHT_3RD = Math.sqrt(
-			Math.pow(StreetsideLayer.TRAFFIC_SIGN_SIZE, 2) - Math.pow(StreetsideLayer.TRAFFIC_SIGN_SIZE / 2d, 2)
-			) / 3;
+  /** The radius of the image marker */
+  private static final int IMG_MARKER_RADIUS = 7;
+  /** The radius of the circular sector that indicates the camera angle */
+  private static final int CA_INDICATOR_RADIUS = 15;
+  /** The angle of the circular sector that indicates the camera angle */
+  private static final int CA_INDICATOR_ANGLE = 40;
+  /** Length of the edge of the small sign, which indicates that traffic signs have been found in an image. */
+  private static final int TRAFFIC_SIGN_SIZE = 6;
+  /** A third of the height of the sign, for easier calculations */
+  private static final double TRAFFIC_SIGN_HEIGHT_3RD = Math.sqrt(
+    Math.pow(TRAFFIC_SIGN_SIZE, 2) - Math.pow(TRAFFIC_SIGN_SIZE / 2d, 2)
+  ) / 3;
 
 	private static final DataSetListenerAdapter DATASET_LISTENER =
@@ -102,33 +109,36 @@
 	}
 
-	/**
-	 * Initializes the Layer.
-	 */
-	private void init() {
-		final DataSet ds = MainApplication.getLayerManager().getEditDataSet();
-		if (ds != null) {
-			ds.addDataSetListener(StreetsideLayer.DATASET_LISTENER);
-		}
-		MainApplication.getLayerManager().addLayer(this);
-		MainApplication.getLayerManager().addActiveLayerChangeListener(this);
-		if (!GraphicsEnvironment.isHeadless()) {
-			setMode(new SelectMode());
-			if (StreetsideDownloader.getMode() == DOWNLOAD_MODE.OSM_AREA) {
-				StreetsideDownloader.downloadOSMArea();
-			}
-			if (StreetsideDownloader.getMode() == DOWNLOAD_MODE.VISIBLE_AREA) {
-				mode.zoomChanged();
-			}
-		}
-		// Does not execute when in headless mode
-		if (Main.main != null && !StreetsideMainDialog.getInstance().isShowing()) {
-			StreetsideMainDialog.getInstance().showDialog();
-		}
-		if (StreetsidePlugin.getMapView() != null) {
-			StreetsideMainDialog.getInstance().getStreetsideImageDisplay().repaint();
-
-			// There is no delete image action for Streetside (Mapillary functionality here removed).
-
-			//getLocationChangeset().addChangesetListener(StreetsideChangesetDialog.getInstance());
+  /**
+   * Initializes the Layer.
+   */
+  private void init() {
+    final DataSet ds = MainApplication.getLayerManager().getEditDataSet();
+    if (ds != null) {
+      ds.addDataSetListener(DATASET_LISTENER);
+    }
+    MainApplication.getLayerManager().addActiveLayerChangeListener(this);
+    if (!GraphicsEnvironment.isHeadless()) {
+      setMode(new SelectMode());
+      if (StreetsideDownloader.getMode() == DOWNLOAD_MODE.OSM_AREA) {
+        MainApplication.worker.execute(StreetsideDownloader::downloadOSMArea);
+      }
+      if (StreetsideDownloader.getMode() == DOWNLOAD_MODE.VISIBLE_AREA) {
+        mode.zoomChanged();
+      }
+    }
+    // Does not execute when in headless mode
+    if (Main.main != null && !StreetsideMainDialog.getInstance().isShowing()) {
+      StreetsideMainDialog.getInstance().showDialog();
+    }
+    if (StreetsidePlugin.getMapView() != null) {
+      StreetsideMainDialog.getInstance().streetsideImageDisplay.repaint();
+      /*StreetsideMainDialog.getInstance()
+        .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
+        .put(KeyStroke.getKeyStroke("DELETE"), "StreetsideDel");
+      StreetsideMainDialog.getInstance().getActionMap()
+        .put("StreetsideDel", new DeleteImageAction());*/
+
+			// There is no delete image action for Streetside (Streetside functionality here removed).
+			getLocationChangeset().addChangesetListener(StreetsideChangesetDialog.getInstance());
 		}
 		createHatchTexture();
@@ -136,60 +146,57 @@
 	}
 
-	public static void invalidateInstance() {
-		if (StreetsideLayer.hasInstance()) {
-			StreetsideLayer.getInstance().invalidate();
-		}
-	}
-
-	/**
-	 * Changes the mode the the given one.
-	 *
-	 * @param mode The mode that is going to be activated.
-	 */
-	public void setMode(AbstractMode mode) {
-		final MapView mv = StreetsidePlugin.getMapView();
-		if (this.mode != null && mv != null) {
-			mv.removeMouseListener(this.mode);
-			mv.removeMouseMotionListener(this.mode);
-			NavigatableComponent.removeZoomChangeListener(this.mode);
-		}
-		this.mode = mode;
-		if (mode != null && mv != null) {
-			mv.setNewCursor(mode.cursor, this);
-			mv.addMouseListener(mode);
-			mv.addMouseMotionListener(mode);
-			NavigatableComponent.addZoomChangeListener(mode);
-			StreetsideUtils.updateHelpText();
-		}
-	}
-
-	private static synchronized void clearInstance() {
-		StreetsideLayer.instance = null;
-	}
-
-	/**
-	 * Returns the unique instance of this class.
-	 *
-	 * @return The unique instance of this class.
-	 */
-	public static synchronized StreetsideLayer getInstance() {
-		if (StreetsideLayer.instance != null) {
-			if (!MainApplication.getLayerManager().containsLayer(StreetsideLayer.instance)) {
-				MainApplication.getLayerManager().addLayer(StreetsideLayer.instance);
-			}
-			return StreetsideLayer.instance;
-		}
-		final StreetsideLayer layer = new StreetsideLayer();
-		StreetsideLayer.instance = layer;
-		layer.init();
-		return layer;
-	}
-
-	/**
-	 * @return if the unique instance of this layer is currently instantiated and added to the {@link LayerManager}
-	 */
-	public static boolean hasInstance() {
-		return StreetsideLayer.instance != null && MainApplication.getLayerManager().containsLayer(StreetsideLayer.instance);
-	}
+  public static void invalidateInstance() {
+    if (hasInstance()) {
+      getInstance().invalidate();
+    }
+  }
+
+  /**
+   * Changes the mode the the given one.
+   *
+   * @param mode The mode that is going to be activated.
+   */
+  public void setMode(AbstractMode mode) {
+    final MapView mv = StreetsidePlugin.getMapView();
+    if (this.mode != null && mv != null) {
+      mv.removeMouseListener(this.mode);
+      mv.removeMouseMotionListener(this.mode);
+      NavigatableComponent.removeZoomChangeListener(this.mode);
+    }
+    this.mode = mode;
+    if (mode != null && mv != null) {
+      mv.setNewCursor(mode.cursor, this);
+      mv.addMouseListener(mode);
+      mv.addMouseMotionListener(mode);
+      NavigatableComponent.addZoomChangeListener(mode);
+      StreetsideUtils.updateHelpText();
+    }
+  }
+
+  private static synchronized void clearInstance() {
+    instance = null;
+  }
+
+  /**
+   * Returns the unique instance of this class.
+   *
+   * @return The unique instance of this class.
+   */
+  public static synchronized StreetsideLayer getInstance() {
+    if (instance != null) {
+      return instance;
+    }
+    final StreetsideLayer layer = new StreetsideLayer();
+    layer.init();
+    instance = layer; // Only set instance field after initialization is complete
+    return instance;
+  }
+
+  /**
+   * @return if the unique instance of this layer is currently instantiated
+   */
+  public static boolean hasInstance() {
+    return instance != null;
+  }
 
 	/**
@@ -200,192 +207,190 @@
 	 */
 	public StreetsideData getData() {
-		return data;
+		return this.data;
 	}
 
-	/**
-	 * Returns the n-nearest image, for n=1 the nearest one is returned, for n=2 the second nearest one and so on.
-	 * The "n-nearest image" is picked from the list of one image from every sequence that is nearest to the currently
-	 * selected image, excluding the sequence to which the selected image belongs.
-	 * @param n the index for picking from the list of "nearest images", beginning from 1
-	 * @return the n-nearest image to the currently selected image
-	 */
-	public synchronized StreetsideImage getNNearestImage(final int n) {
-		return n >= 1 && n <= nearestImages.length ? nearestImages[n - 1] : null;
-	}
-
-	/**
-	   * Returns the {@link StreetsideLocationChangeset} object, which acts as the database of the
-	   * Layer.
-	   *
-	   * @return The {@link StreetsideData} object that stores the database.
-	   */
-	  public StreetsideLocationChangeset getLocationChangeset() {
-	    return locationChangeset;
-	  }
+  /**
+   * Returns the {@link StreetsideLocationChangeset} object, which acts as the database of the
+   * Layer.
+   *
+   * @return The {@link StreetsideData} object that stores the database.
+   */
+  public StreetsideLocationChangeset getLocationChangeset() {
+    return locationChangeset;
+  }
+
+  /**
+   * Returns the n-nearest image, for n=1 the nearest one is returned, for n=2 the second nearest one and so on.
+   * The "n-nearest image" is picked from the list of one image from every sequence that is nearest to the currently
+   * selected image, excluding the sequence to which the selected image belongs.
+   * @param n the index for picking from the list of "nearest images", beginning from 1
+   * @return the n-nearest image to the currently selected image
+   */
+  public synchronized StreetsideImage getNNearestImage(final int n) {
+    return n >= 1 && n <= nearestImages.length ? nearestImages[n - 1] : null;
+  }
+
+  @Override
+  public synchronized void destroy() {
+    clearInstance();
+    setMode(null);
+    StreetsideRecord.getInstance().reset();
+    AbstractMode.resetThread();
+    StreetsideDownloader.stopAll();
+    if (StreetsideMainDialog.hasInstance()) {
+      StreetsideMainDialog.getInstance().setImage(null);
+      StreetsideMainDialog.getInstance().updateImage();
+    }
+    final MapView mv = StreetsidePlugin.getMapView();
+    if (mv != null) {
+      mv.removeMouseListener(mode);
+      mv.removeMouseMotionListener(mode);
+    }
+    try {
+      MainApplication.getLayerManager().removeActiveLayerChangeListener(this);
+      if (MainApplication.getLayerManager().getEditDataSet() != null) {
+        MainApplication.getLayerManager().getEditDataSet().removeDataSetListener(DATASET_LISTENER);
+      }
+    } catch (IllegalArgumentException e) {
+      // TODO: It would be ideal, to fix this properly. But for the moment let's catch this, for when a listener has already been removed.
+    }
+    super.destroy();
+  }
 
 
 	@Override
-	public synchronized void destroy() {
-		// TODO: Add destroy code for CubemapBuilder, et al.? @rrh
-		StreetsideLayer.clearInstance();
-		setMode(null);
-		StreetsideRecord.getInstance().reset();
-		AbstractMode.resetThread();
-		StreetsideDownloader.stopAll();
-		if (StreetsideMainDialog.hasInstance()) {
-			StreetsideMainDialog.getInstance().setImage(null);
-			StreetsideMainDialog.getInstance().updateImage();
-		}
-		final MapView mv = StreetsidePlugin.getMapView();
-		if (mv != null) {
-			mv.removeMouseListener(mode);
-			mv.removeMouseMotionListener(mode);
-		}
-		try {
-			MainApplication.getLayerManager().removeActiveLayerChangeListener(this);
-			if (MainApplication.getLayerManager().getEditDataSet() != null) {
-				MainApplication.getLayerManager().getEditDataSet().removeDataSetListener(StreetsideLayer.DATASET_LISTENER);
-			}
-		} catch (final IllegalArgumentException e) {
-			// TODO: It would be ideal, to fix this properly. But for the moment let's catch this, for when a listener has already been removed.
-		}
-		super.destroy();
-	}
-
-	@Override
-	public boolean isModified() {
-		// TODO: Add cubemap modification here? @rrh
-		return data.getImages().parallelStream().anyMatch(StreetsideAbstractImage::isModified);
-	}
-
-	@Override
-	public void setVisible(boolean visible) {
-		super.setVisible(visible);
-		getData().getImages().parallelStream().forEach(img -> img.setVisible(visible));
-		if (MainApplication.getMap() != null) {
-			//StreetsideFilterDialog.getInstance().refresh();
-		}
-	}
-
-	/**
-	 * Initialize the hatch pattern used to paint the non-downloaded area.
-	 */
-	private void createHatchTexture() {
-		final BufferedImage bi = new BufferedImage(15, 15, BufferedImage.TYPE_INT_ARGB);
-		final Graphics2D big = bi.createGraphics();
-		big.setColor(StreetsideProperties.BACKGROUND.get());
-		final Composite comp = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.3f);
-		big.setComposite(comp);
-		big.fillRect(0, 0, 15, 15);
-		big.setColor(StreetsideProperties.OUTSIDE_DOWNLOADED_AREA.get());
-		big.drawLine(0, 15, 15, 0);
-		final Rectangle r = new Rectangle(0, 0, 15, 15);
-		hatched = new TexturePaint(bi, r);
-	}
-
-	@Override
-	public synchronized void paint(final Graphics2D g, final MapView mv, final Bounds box) {
-		g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
-		if (MainApplication.getLayerManager().getActiveLayer() == this) {
-			// paint remainder
-			g.setPaint(hatched);
-			g.fill(MapViewGeometryUtil.getNonDownloadedArea(mv, data.getBounds()));
-		}
-
-		// Draw the blue and red line
-		synchronized (StreetsideLayer.class) {
-			final StreetsideAbstractImage selectedImg = data.getSelectedImage();
-			for (int i = 0; i < nearestImages.length && selectedImg != null; i++) {
-				if (i == 0) {
-					g.setColor(Color.RED);
-				} else {
-					g.setColor(Color.BLUE);
-				}
-				final Point selected = mv.getPoint(selectedImg.getMovingLatLon());
-				final Point p = mv.getPoint(nearestImages[i].getMovingLatLon());
-				g.draw(new Line2D.Double(p.getX(), p.getY(), selected.getX(), selected.getY()));
-			}
-		}
-
-		// Draw sequence line
-		g.setStroke(new BasicStroke(2));
-		final StreetsideAbstractImage selectedImage = getData().getSelectedImage();
-		for (final StreetsideSequence seq : getData().getSequences()) {
-			if (seq.getImages().contains(selectedImage)) {
-				g.setColor(
-						seq.getId() == null ? StreetsideColorScheme.SEQ_IMPORTED_SELECTED : StreetsideColorScheme.SEQ_SELECTED
-						);
-			} else {
-				g.setColor(
-						seq.getId() == null ? StreetsideColorScheme.SEQ_IMPORTED_UNSELECTED : StreetsideColorScheme.SEQ_UNSELECTED
-						);
-			}
-			g.draw(MapViewGeometryUtil.getSequencePath(mv, seq));
-		}
-		/*for (final StreetsideAbstractImage imageAbs : data.getImages()) {
-			if (imageAbs.isVisible() && mv != null && mv.contains(mv.getPoint(imageAbs.getMovingLatLon()))) {
-				drawImageMarker(g, imageAbs);
-			}
-		}*/
-		if (mode instanceof JoinMode) {
-			mode.paint(g, mv, box);
-		}
-	}
-
-	/*
-	 * Draws an image marker onto the given Graphics context.
-	 * @param g the Graphics context
-	 * @param img the image to be drawn onto the Graphics context
-	 */
-	/*private void drawImageMarker(final Graphics2D g, final StreetsideAbstractImage img) {
-		if (img == null || img.getLatLon() == null) {
-			Logging.warn("An image is not painted, because it is null or has no LatLon!");
-			return;
-		}
-		final StreetsideAbstractImage selectedImg = getData().getSelectedImage();
-		final Point p = MainApplication.getMap().mapView.getPoint(img.getMovingLatLon());
-
-		// Determine colors
-		final Color markerC;
-		final Color directionC;
-		if (selectedImg != null && getData().getMultiSelectedImages().contains(img)) {
-			markerC = img instanceof StreetsideImportedImage
-					? StreetsideColorScheme.SEQ_IMPORTED_HIGHLIGHTED
-							: StreetsideColorScheme.SEQ_HIGHLIGHTED;
-			directionC = img instanceof StreetsideImportedImage
-					? StreetsideColorScheme.SEQ_IMPORTED_HIGHLIGHTED_CA
-							: StreetsideColorScheme.SEQ_HIGHLIGHTED_CA;
-		} else if (selectedImg != null && selectedImg.getSequence() != null && selectedImg.getSequence().equals(img.getSequence())) {
-			markerC = img instanceof StreetsideImportedImage
-					? StreetsideColorScheme.SEQ_IMPORTED_SELECTED
-							: StreetsideColorScheme.SEQ_SELECTED;
-			directionC = img instanceof StreetsideImportedImage
-					? StreetsideColorScheme.SEQ_IMPORTED_SELECTED_CA
-							: StreetsideColorScheme.SEQ_SELECTED_CA;
-		} else {
-			markerC = img instanceof StreetsideImportedImage
-					? StreetsideColorScheme.SEQ_IMPORTED_UNSELECTED
-							: StreetsideColorScheme.SEQ_UNSELECTED;
-			directionC = img instanceof StreetsideImportedImage
-					? StreetsideColorScheme.SEQ_IMPORTED_UNSELECTED_CA
-							: StreetsideColorScheme.SEQ_UNSELECTED_CA;
-		}
-
-		// Paint direction indicator
-		g.setColor(directionC);
-		g.fillArc(p.x - StreetsideLayer.CA_INDICATOR_RADIUS, p.y - StreetsideLayer.CA_INDICATOR_RADIUS, 2 * StreetsideLayer.CA_INDICATOR_RADIUS, 2 * StreetsideLayer.CA_INDICATOR_RADIUS, (int) (90 - img.getMovingHe() - StreetsideLayer.CA_INDICATOR_ANGLE / 2d), StreetsideLayer.CA_INDICATOR_ANGLE);
-		// Paint image marker
-		g.setColor(markerC);
-		g.fillOval(p.x - StreetsideLayer.IMG_MARKER_RADIUS, p.y - StreetsideLayer.IMG_MARKER_RADIUS, 2 * StreetsideLayer.IMG_MARKER_RADIUS, 2 * StreetsideLayer.IMG_MARKER_RADIUS);
-
-		// Paint highlight for selected or highlighted images
-		if (img.equals(getData().getHighlightedImage()) || getData().getMultiSelectedImages().contains(img)) {
-			g.setColor(Color.WHITE);
-			g.setStroke(new BasicStroke(2));
-			g.drawOval(p.x - StreetsideLayer.IMG_MARKER_RADIUS, p.y - StreetsideLayer.IMG_MARKER_RADIUS, 2 * StreetsideLayer.IMG_MARKER_RADIUS, 2 * StreetsideLayer.IMG_MARKER_RADIUS);
-		}
-
-		// TODO: reimplement detections for Bing Metadata? RRH
-		if (img instanceof StreetsideImage && !((StreetsideImage) img).getDetections().isEmpty()) {
+  public boolean isModified() {
+    return this.data.getImages().parallelStream().anyMatch(StreetsideAbstractImage::isModified);
+  }
+
+  @Override
+  public void setVisible(boolean visible) {
+    super.setVisible(visible);
+    getData().getImages().parallelStream().forEach(img -> img.setVisible(visible));
+    if (MainApplication.getMap() != null) {
+      //StreetsideFilterDialog.getInstance().refresh();
+    }
+  }
+
+  /**
+   * Initialize the hatch pattern used to paint the non-downloaded area.
+   */
+  private void createHatchTexture() {
+    BufferedImage bi = new BufferedImage(15, 15, BufferedImage.TYPE_INT_ARGB);
+    Graphics2D big = bi.createGraphics();
+    big.setColor(StreetsideProperties.BACKGROUND.get());
+    Composite comp = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.3f);
+    big.setComposite(comp);
+    big.fillRect(0, 0, 15, 15);
+    big.setColor(StreetsideProperties.OUTSIDE_DOWNLOADED_AREA.get());
+    big.drawLine(0, 15, 15, 0);
+    Rectangle r = new Rectangle(0, 0, 15, 15);
+    this.hatched = new TexturePaint(bi, r);
+  }
+
+  @Override
+  public synchronized void paint(final Graphics2D g, final MapView mv, final Bounds box) {
+    g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+    if (MainApplication.getLayerManager().getActiveLayer() == this) {
+      // paint remainder
+      g.setPaint(hatched);
+      g.fill(MapViewGeometryUtil.getNonDownloadedArea(mv, this.data.getBounds()));
+    }
+
+    // Draw the blue and red line
+    synchronized (StreetsideLayer.class) {
+      final StreetsideAbstractImage selectedImg = data.getSelectedImage();
+      for (int i = 0; i < nearestImages.length && selectedImg != null; i++) {
+        if (i == 0) {
+          g.setColor(Color.RED);
+        } else {
+          g.setColor(Color.BLUE);
+        }
+        final Point selected = mv.getPoint(selectedImg.getMovingLatLon());
+        final Point p = mv.getPoint(nearestImages[i].getMovingLatLon());
+        g.draw(new Line2D.Double(p.getX(), p.getY(), selected.getX(), selected.getY()));
+      }
+    }
+
+    // Draw sequence line
+    g.setStroke(new BasicStroke(2));
+    final StreetsideAbstractImage selectedImage = getData().getSelectedImage();
+    for (StreetsideSequence seq : getData().getSequences()) {
+      if (seq.getImages().contains(selectedImage)) {
+        g.setColor(
+          seq.getId() == null ? StreetsideColorScheme.SEQ_IMPORTED_SELECTED : StreetsideColorScheme.SEQ_SELECTED
+        );
+      } else {
+        g.setColor(
+          seq.getId() == null ? StreetsideColorScheme.SEQ_IMPORTED_UNSELECTED : StreetsideColorScheme.SEQ_UNSELECTED
+        );
+      }
+      g.draw(MapViewGeometryUtil.getSequencePath(mv, seq));
+    }
+    for (StreetsideAbstractImage imageAbs : this.data.getImages()) {
+      if (imageAbs.isVisible() && mv != null && mv.contains(mv.getPoint(imageAbs.getMovingLatLon()))) {
+        drawImageMarker(g, imageAbs);
+      }
+    }
+    if (this.mode instanceof JoinMode) {
+      this.mode.paint(g, mv, box);
+    }
+  }
+
+  /**
+   * Draws an image marker onto the given Graphics context.
+   * @param g the Graphics context
+   * @param img the image to be drawn onto the Graphics context
+   */
+  private void drawImageMarker(final Graphics2D g, final StreetsideAbstractImage img) {
+    if (img == null || img.getLatLon() == null) {
+      Logging.warn("An image is not painted, because it is null or has no LatLon!");
+      return;
+    }
+    final StreetsideAbstractImage selectedImg = getData().getSelectedImage();
+    final Point p = MainApplication.getMap().mapView.getPoint(img.getMovingLatLon());
+
+    // Determine colors
+    final Color markerC;
+    final Color directionC;
+    if (selectedImg != null && getData().getMultiSelectedImages().contains(img)) {
+      markerC = img instanceof StreetsideImportedImage
+        ? StreetsideColorScheme.SEQ_IMPORTED_HIGHLIGHTED
+        : StreetsideColorScheme.SEQ_HIGHLIGHTED;
+      directionC = img instanceof StreetsideImportedImage
+        ? StreetsideColorScheme.SEQ_IMPORTED_HIGHLIGHTED_CA
+        : StreetsideColorScheme.SEQ_HIGHLIGHTED_CA;
+    } else if (selectedImg != null && selectedImg.getSequence() != null && selectedImg.getSequence().equals(img.getSequence())) {
+      markerC = img instanceof StreetsideImportedImage
+        ? StreetsideColorScheme.SEQ_IMPORTED_SELECTED
+        : StreetsideColorScheme.SEQ_SELECTED;
+      directionC = img instanceof StreetsideImportedImage
+        ? StreetsideColorScheme.SEQ_IMPORTED_SELECTED_CA
+        : StreetsideColorScheme.SEQ_SELECTED_CA;
+    } else {
+      markerC = img instanceof StreetsideImportedImage
+        ? StreetsideColorScheme.SEQ_IMPORTED_UNSELECTED
+        : StreetsideColorScheme.SEQ_UNSELECTED;
+      directionC = img instanceof StreetsideImportedImage
+        ? StreetsideColorScheme.SEQ_IMPORTED_UNSELECTED_CA
+        : StreetsideColorScheme.SEQ_UNSELECTED_CA;
+    }
+
+    // Paint direction indicator
+    g.setColor(directionC);
+    g.fillArc(p.x - CA_INDICATOR_RADIUS, p.y - CA_INDICATOR_RADIUS, 2 * CA_INDICATOR_RADIUS, 2 * CA_INDICATOR_RADIUS, (int) (90 - img.getMovingHe() - CA_INDICATOR_ANGLE / 2d), CA_INDICATOR_ANGLE);
+    // Paint image marker
+    g.setColor(markerC);
+    g.fillOval(p.x - IMG_MARKER_RADIUS, p.y - IMG_MARKER_RADIUS, 2 * IMG_MARKER_RADIUS, 2 * IMG_MARKER_RADIUS);
+
+    // Paint highlight for selected or highlighted images
+    if (img.equals(getData().getHighlightedImage()) || getData().getMultiSelectedImages().contains(img)) {
+      g.setColor(Color.WHITE);
+      g.setStroke(new BasicStroke(2));
+      g.drawOval(p.x - IMG_MARKER_RADIUS, p.y - IMG_MARKER_RADIUS, 2 * IMG_MARKER_RADIUS, 2 * IMG_MARKER_RADIUS);
+    }
+
+
+		/*if (img instanceof StreetsideImage && !((StreetsideImage) img).getDetections().isEmpty()) {
 			final Path2D trafficSign = new Path2D.Double();
 			trafficSign.moveTo(p.getX() - StreetsideLayer.TRAFFIC_SIGN_SIZE / 2d, p.getY() - StreetsideLayer.TRAFFIC_SIGN_HEIGHT_3RD);
@@ -398,215 +403,181 @@
 			g.setColor(Color.RED);
 			g.draw(trafficSign);
-		}
-	}*/
-
-	@Override
-	public Icon getIcon() {
-		return StreetsidePlugin.LOGO.setSize(ImageSizes.LAYER).get();
+		}*/
 	}
 
-	@Override
-	public boolean isMergable(Layer other) {
-		return false;
-	}
-
-	@Override
-	public void mergeFrom(Layer from) {
-		throw new UnsupportedOperationException(
-				"This layer does not support merging yet");
-	}
-
-	@Override
-	public Action[] getMenuEntries() {
-		return new Action[]{
-				LayerListDialog.getInstance().createShowHideLayerAction(),
-				LayerListDialog.getInstance().createDeleteLayerAction(),
-				new LayerListPopup.InfoAction(this)
-		};
-	}
-
-	@Override
-	public Object getInfoComponent() {
-		final IntSummaryStatistics seqSizeStats = getData().getSequences().stream().mapToInt(seq -> seq.getImages().size()).summaryStatistics();
-		return new StringBuilder(I18n.tr("Streetside layer"))
-				.append("\n")
-				.append(I18n.tr(
-						"{0} sequences, each containing between {1} and {2} images (ø {3})",
-						getData().getSequences().size(),
-						seqSizeStats.getCount() <= 0 ? 0 : seqSizeStats.getMin(),
-								seqSizeStats.getCount() <= 0 ? 0 : seqSizeStats.getMax(),
-										seqSizeStats.getAverage()
-						))
-				.append("\n\n")
-				.append(I18n.tr(
-						"{0} imported images",
-						getData().getImages().stream().filter(i -> i instanceof StreetsideImportedImage).count()
-						))
-				.append("\n+ ")
-				.append(I18n.tr(
-						"{0} downloaded images",
-						getData().getImages().stream().filter(i -> i instanceof StreetsideImage).count()
-						))
-				.append("\n= ")
-				.append(I18n.tr(
-						"{0} images in total",
-						getData().getImages().size()
-						)).toString();
-	}
-
-	@Override
-	public String getToolTipText() {
-		return I18n.tr("{0} images in {1} sequences", getData().getImages().size(), getData().getSequences().size());
-	}
-
-	@Override
-	public void activeOrEditLayerChanged(ActiveLayerChangeEvent e) {
-		if (MainApplication.getLayerManager().getActiveLayer() == this) {
-			StreetsideUtils.updateHelpText();
-		}
-
-		if (MainApplication.getLayerManager().getEditLayer() != e.getPreviousDataLayer()) {
-			if (MainApplication.getLayerManager().getEditLayer() != null) {
-				MainApplication.getLayerManager().getEditLayer().getDataSet().addDataSetListener(StreetsideLayer.DATASET_LISTENER);
-			}
-			if (e.getPreviousDataLayer() != null) {
-				e.getPreviousDataLayer().getDataSet().removeDataSetListener(StreetsideLayer.DATASET_LISTENER);
-			}
-		}
-	}
-
-	@Override
-	public void visitBoundingBox(BoundingXYVisitor v) {
-	}
-
-	/* (non-Javadoc)
-	 * @see org.openstreetmap.josm.plugins.streetside.StreetsideDataListener#imagesAdded()
-	 */
-	@Override
-	public void imagesAdded() {
-		// TODO: Never used - could this be of use? @rrh
-		updateNearestImages();
-	}
-
-	/* (non-Javadoc)
-	 * @see org.openstreetmap.josm.plugins.mapillary.StreetsideDataListener#selectedImageChanged(org.openstreetmap.josm.plugins.mapillary.MapillaryAbstractImage, org.openstreetmap.josm.plugins.mapillary.MapillaryAbstractImage)
-	 */
-	@Override
-	public void selectedImageChanged(StreetsideAbstractImage oldImage, StreetsideAbstractImage newImage) {
-		updateNearestImages();
-	}
-
-	/**
-	 * Returns the closest images belonging to a different sequence and
-	 * different from the specified target image.
-	 *
-	 * @param target the image for which you want to find the nearest other images
-	 * @param limit the maximum length of the returned array
-	 * @return An array containing the closest images belonging to different sequences sorted by distance from target.
-	 */
-	private StreetsideImage[] getNearestImagesFromDifferentSequences(StreetsideAbstractImage target, int limit) {
-		return data.getSequences().parallelStream()
-				.filter(seq -> seq.getId() != null && !seq.getId().equals(target.getSequence().getId()))
-				.map(seq -> { // Maps sequence to image from sequence that is nearest to target
-					final Optional<StreetsideAbstractImage> resImg = seq.getImages().parallelStream()
-							.filter(img -> img instanceof StreetsideImage && img.isVisible())
-							.min(new NearestImgToTargetComparator(target));
-					return resImg.orElse(null);
-				})
-				.filter(img -> // Filters out images too far away from target
-				img != null &&
-				img.getMovingLatLon().greatCircleDistance(target.getMovingLatLon())
-				< StreetsideProperties.SEQUENCE_MAX_JUMP_DISTANCE.get()
-						)
-				.sorted(new NearestImgToTargetComparator(target))
-				.limit(limit)
-				.toArray(StreetsideImage[]::new);
-	}
-
-	/*
-	 * Returns the closest images belonging to a different sequence and
-	 * different from the specified target image.
-	 *
-	 * @param target the image for which you want to find the nearest other images
-	 * @param limit the maximum length of the returned array
-	 * @return An array containing the closest images belonging to different sequences sorted by distance from target.
-	 */
-	/*private StreetsideCubemap[] getNearestCubemapsFromDifferentSequences(StreetsideAbstractImage target, int limit) {
-		return data.getSequences().parallelStream()
-				.filter(seq -> seq.getId() != null && !seq.getId().equals(target.getSequence().getId()))
-				.map(seq -> { // Maps sequence to image from sequence that is nearest to target
-					final Optional<StreetsideAbstractImage> resCb = seq.getImages().parallelStream()
-							.filter(cb -> cb instanceof StreetsideCubemap && cb.isVisible())
-							.min(new NearestCbToTargetComparator(target));
-					return resCb.orElse(null);
-				})
-				.filter(cb -> // Filters out images too far away from target
-				cb != null &&
-				cb.getMovingLatLon().greatCircleDistance(target.getMovingLatLon())
-				< StreetsideProperties.SEQUENCE_MAX_JUMP_DISTANCE.get()
-						)
-				.sorted(new NearestCbToTargetComparator(target))
-				.limit(limit)
-				.toArray(StreetsideCubemap[]::new);
-	}*/
-
-	private synchronized void updateNearestImages() {
-		final StreetsideAbstractImage selected = data.getSelectedImage();
-		if (selected != null) {
-			// TODO: could this be used to pre-cache cubemaps? @rrh
-			nearestImages = getNearestImagesFromDifferentSequences(selected, 2);
-		} else {
-			nearestImages = new StreetsideImage[0];
-		}
-		if (MainApplication.isDisplayingMapView()) {
-			StreetsideMainDialog.getInstance().redButton.setEnabled(nearestImages.length >= 1);
-			StreetsideMainDialog.getInstance().blueButton.setEnabled(nearestImages.length >= 2);
-		}
-		if (nearestImages.length >= 1) {
-			CacheUtils.downloadPicture(nearestImages[0]);
-			// TODO: download/pre-caches cubemaps here?
-			//CacheUtils.downloadCubemap(nearestImages[0]);
-			if (nearestImages.length >= 2) {
-				CacheUtils.downloadPicture(nearestImages[1]);
-				// TODO: download/pre-caches cubemaps here?
-				//CacheUtils.downloadCubemap(nearestImages[1]);
-			}
-		}
-	}
-
-	private static class NearestImgToTargetComparator implements Comparator<StreetsideAbstractImage> {
-		private final StreetsideAbstractImage target;
-
-		public NearestImgToTargetComparator(StreetsideAbstractImage target) {
-			this.target = target;
-		}
-		/* (non-Javadoc)
-		 * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
-		 */
-		@Override
-		public int compare(StreetsideAbstractImage img1, StreetsideAbstractImage img2) {
-			return (int) Math.signum(
-					img1.getMovingLatLon().greatCircleDistance(target.getMovingLatLon()) -
-					img2.getMovingLatLon().greatCircleDistance(target.getMovingLatLon())
-					);
-		}
-	}
-
-	private static class NearestCbToTargetComparator implements Comparator<StreetsideAbstractImage> {
-		private final StreetsideAbstractImage target;
-
-		public NearestCbToTargetComparator(StreetsideAbstractImage target) {
-			this.target = target;
-		}
-		/* (non-Javadoc)
-		 * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
-		 */
-		@Override
-		public int compare(StreetsideAbstractImage img1, StreetsideAbstractImage img2) {
-			return (int) Math.signum(
-					img1.getMovingLatLon().greatCircleDistance(target.getMovingLatLon()) -
-					img2.getMovingLatLon().greatCircleDistance(target.getMovingLatLon())
-					);
-		}
-	}
+  @Override
+  public Icon getIcon() {
+    return StreetsidePlugin.LOGO.setSize(ImageSizes.LAYER).get();
+  }
+
+  @Override
+  public boolean isMergable(Layer other) {
+    return false;
+  }
+
+  @Override
+  public void mergeFrom(Layer from) {
+    throw new UnsupportedOperationException(
+      "This layer does not support merging yet");
+  }
+
+  @Override
+  public Action[] getMenuEntries() {
+    return new Action[]{
+      LayerListDialog.getInstance().createShowHideLayerAction(),
+      LayerListDialog.getInstance().createDeleteLayerAction(),
+      new LayerListPopup.InfoAction(this)
+    };
+  }
+
+  @Override
+  public Object getInfoComponent() {
+    IntSummaryStatistics seqSizeStats = getData().getSequences().stream().mapToInt(seq -> seq.getImages().size()).summaryStatistics();
+    return new StringBuilder(I18n.tr("Streetside layer"))
+      .append('\n')
+      .append(I18n.tr(
+        "{0} sequences, each containing between {1} and {2} images (ø {3})",
+        getData().getSequences().size(),
+        seqSizeStats.getCount() <= 0 ? 0 : seqSizeStats.getMin(),
+        seqSizeStats.getCount() <= 0 ? 0 : seqSizeStats.getMax(),
+        seqSizeStats.getAverage()
+      ))
+      .append("\n\n")
+      .append(I18n.tr(
+        "{0} imported images",
+        getData().getImages().stream().filter(i -> i instanceof StreetsideImportedImage).count()
+      ))
+      .append("\n+ ")
+      .append(I18n.tr(
+        "{0} downloaded images",
+        getData().getImages().stream().filter(i -> i instanceof StreetsideImage).count()
+      ))
+      .append("\n= ")
+      .append(I18n.tr(
+        "{0} images in total",
+        getData().getImages().size()
+      )).toString();
+  }
+
+  @Override
+  public String getToolTipText() {
+    return I18n.tr("{0} images in {1} sequences", getData().getImages().size(), getData().getSequences().size());
+  }
+
+  @Override
+  public void activeOrEditLayerChanged(ActiveLayerChangeEvent e) {
+    if (MainApplication.getLayerManager().getActiveLayer() == this) {
+      StreetsideUtils.updateHelpText();
+    }
+
+    if (MainApplication.getLayerManager().getEditLayer() != e.getPreviousDataLayer()) {
+      if (MainApplication.getLayerManager().getEditLayer() != null) {
+        MainApplication.getLayerManager().getEditLayer().getDataSet().addDataSetListener(DATASET_LISTENER);
+      }
+      if (e.getPreviousDataLayer() != null) {
+        e.getPreviousDataLayer().getDataSet().removeDataSetListener(DATASET_LISTENER);
+      }
+    }
+  }
+
+  @Override
+  public void visitBoundingBox(BoundingXYVisitor v) {
+  }
+
+  /* (non-Javadoc)
+   * @see org.openstreetmap.josm.plugins.streetside.StreetsideDataListener#imagesAdded()
+   */
+  @Override
+  public void imagesAdded() {
+    updateNearestImages();
+  }
+
+  /* (non-Javadoc)
+   * @see org.openstreetmap.josm.plugins.streetside.StreetsideDataListener#selectedImageChanged(org.openstreetmap.josm.plugins.streetside.StreetsideAbstractImage, org.openstreetmap.josm.plugins.streetside.StreetsideAbstractImage)
+   */
+  @Override
+  public void selectedImageChanged(StreetsideAbstractImage oldImage, StreetsideAbstractImage newImage) {
+    updateNearestImages();
+  }
+
+  /**
+   * Returns the closest images belonging to a different sequence and
+   * different from the specified target image.
+   *
+   * @param target the image for which you want to find the nearest other images
+   * @param limit the maximum length of the returned array
+   * @return An array containing the closest images belonging to different sequences sorted by distance from target.
+   */
+  private StreetsideImage[] getNearestImagesFromDifferentSequences(StreetsideAbstractImage target, int limit) {
+    return data.getSequences().parallelStream()
+      .filter(seq -> seq.getId() != null && !seq.getId().equals(target.getSequence().getId()))
+      .map(seq -> { // Maps sequence to image from sequence that is nearest to target
+        Optional<StreetsideAbstractImage> resImg = seq.getImages().parallelStream()
+          .filter(img -> img instanceof StreetsideImage && img.isVisible())
+          .min(new NearestImgToTargetComparator(target));
+        return resImg.orElse(null);
+      })
+      .filter(img -> // Filters out images too far away from target
+        img != null &&
+        img.getMovingLatLon().greatCircleDistance(target.getMovingLatLon())
+          < StreetsideProperties.SEQUENCE_MAX_JUMP_DISTANCE.get()
+       )
+      .sorted(new NearestImgToTargetComparator(target))
+      .limit(limit)
+      .toArray(StreetsideImage[]::new);
+  }
+
+  private synchronized void updateNearestImages() {
+    final StreetsideAbstractImage selected = data.getSelectedImage();
+    if (selected != null) {
+      nearestImages = getNearestImagesFromDifferentSequences(selected, 2);
+    } else {
+      nearestImages = new StreetsideImage[0];
+    }
+    if (MainApplication.isDisplayingMapView()) {
+      StreetsideMainDialog.getInstance().redButton.setEnabled(nearestImages.length >= 1);
+      StreetsideMainDialog.getInstance().blueButton.setEnabled(nearestImages.length >= 2);
+    }
+    if (nearestImages.length >= 1) {
+      CacheUtils.downloadPicture(nearestImages[0]);
+      if (nearestImages.length >= 2) {
+        CacheUtils.downloadPicture(nearestImages[1]);
+      }
+    }
+  }
+
+  /**
+   * Action used to delete images.
+   *
+   * @author nokutu
+   */
+  /*private class DeleteImageAction extends AbstractAction {
+
+    private static final long serialVersionUID = -982809854631863962L;
+
+    @Override
+    public void actionPerformed(ActionEvent e) {
+      if (instance != null)
+        StreetsideRecord.getInstance().addCommand(
+          new CommandDelete(getData().getMultiSelectedImages()));
+    }
+  }*/
+
+  private static class NearestImgToTargetComparator implements Comparator<StreetsideAbstractImage> {
+    private final StreetsideAbstractImage target;
+
+    public NearestImgToTargetComparator(StreetsideAbstractImage target) {
+      this.target = target;
+    }
+    /* (non-Javadoc)
+     * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
+     */
+    @Override
+    public int compare(StreetsideAbstractImage img1, StreetsideAbstractImage img2) {
+      return (int) Math.signum(
+        img1.getMovingLatLon().greatCircleDistance(target.getMovingLatLon()) -
+        img2.getMovingLatLon().greatCircleDistance(target.getMovingLatLon())
+      );
+    }
+  }
 }
Index: /applications/editors/josm/plugins/MicrosoftStreetside/src/org/openstreetmap/josm/plugins/streetside/StreetsidePlugin.java
===================================================================
--- /applications/editors/josm/plugins/MicrosoftStreetside/src/org/openstreetmap/josm/plugins/streetside/StreetsidePlugin.java	(revision 34328)
+++ /applications/editors/josm/plugins/MicrosoftStreetside/src/org/openstreetmap/josm/plugins/streetside/StreetsidePlugin.java	(revision 34329)
@@ -49,4 +49,5 @@
       MainMenu.add(MainApplication.getMenu().moreToolsMenu, WALK_ACTION, false);
       //MainMenu.add(MainApplication.getMenu().imagerySubMenu, new MapObjectLayerAction(), false);
+      //MainMenu.add(MainApplication.getMenu().imagerySubMenu, new MapObjectLayerAction(), false);
     }
   }
@@ -67,5 +68,5 @@
 
   static StreetsideDataListener[] getStreetsideDataListeners() {
-	return new StreetsideDataListener[]{/*UPLOAD_ACTION,*/ WALK_ACTION, ZOOM_ACTION, CubemapBuilder.getInstance()};
+	return new StreetsideDataListener[]{WALK_ACTION, ZOOM_ACTION, CubemapBuilder.getInstance()};
   }
 
