diff --git a/src/org/openstreetmap/josm/gui/layer/geoimage/ImageDisplay.java b/src/org/openstreetmap/josm/gui/layer/geoimage/ImageDisplay.java
index f8fab1e1c6..daac93f6cd 100644
--- a/src/org/openstreetmap/josm/gui/layer/geoimage/ImageDisplay.java
+++ b/src/org/openstreetmap/josm/gui/layer/geoimage/ImageDisplay.java
@@ -260,7 +260,7 @@ public class ImageDisplay extends JComponent implements Destroyable, PreferenceC
         @Override
         public void run() {
             try {
-                Dimension target = new Dimension(MAX_WIDTH.get(), MAX_WIDTH.get());
+                Dimension target = new Dimension(4 * MAX_WIDTH.get(), 4 * MAX_WIDTH.get());
                 BufferedImage img = entry.read(target);
                 if (img == null) {
                     synchronized (ImageDisplay.this) {
diff --git a/src/org/openstreetmap/josm/gui/layer/geoimage/viewers/projections/Equirectangular.java b/src/org/openstreetmap/josm/gui/layer/geoimage/viewers/projections/Equirectangular.java
index a85acbcc88..643d401cec 100644
--- a/src/org/openstreetmap/josm/gui/layer/geoimage/viewers/projections/Equirectangular.java
+++ b/src/org/openstreetmap/josm/gui/layer/geoimage/viewers/projections/Equirectangular.java
@@ -14,9 +14,9 @@ import java.util.Set;
 
 import org.openstreetmap.josm.data.imagery.street_level.Projections;
 import org.openstreetmap.josm.gui.layer.geoimage.ImageDisplay;
-import org.openstreetmap.josm.gui.util.GuiHelper;
 import org.openstreetmap.josm.gui.util.imagery.CameraPlane;
 import org.openstreetmap.josm.gui.util.imagery.Vector3D;
+import org.openstreetmap.josm.tools.Logging;
 
 /**
  * A class for showing 360 images that use the equirectangular projection
@@ -34,11 +34,17 @@ public class Equirectangular extends ComponentAdapter implements IImageViewer {
 
     @Override
     public void paintImage(Graphics g, BufferedImage image, Rectangle target, Rectangle visibleRect) {
-        this.cameraPlane.mapping(image, this.offscreenImage);
+        final CameraPlane currentCameraPlane;
+        final BufferedImage currentOffscreenImage;
+        synchronized (this) {
+            currentCameraPlane = this.cameraPlane;
+            currentOffscreenImage = this.offscreenImage;
+        }
+        currentCameraPlane.mapping(image, currentOffscreenImage);
         if (target == null) {
-            target = new Rectangle(0, 0, offscreenImage.getWidth(null), offscreenImage.getHeight(null));
+            target = new Rectangle(0, 0, currentOffscreenImage.getWidth(null), currentOffscreenImage.getHeight(null));
         }
-        g.drawImage(offscreenImage, target.x, target.y, target.x + target.width, target.y + target.height,
+        g.drawImage(currentOffscreenImage, target.x, target.y, target.x + target.width, target.y + target.height,
                 visibleRect.x, visibleRect.y, visibleRect.x + visibleRect.width, visibleRect.y + visibleRect.height,
                 null);
     }
@@ -61,17 +67,27 @@ public class Equirectangular extends ComponentAdapter implements IImageViewer {
             final ImageDisplay imgDisplay = (ImageDisplay) component;
             // FIXME: Do something so that the types of the images are the same between the offscreenImage and
             // the image entry
-            this.offscreenImage = new BufferedImage(imgDisplay.getWidth(), imgDisplay.getHeight(),
+            final CameraPlane currentCameraPlane;
+            synchronized (this) {
+                currentCameraPlane = this.cameraPlane;
+            }
+            final BufferedImage temporaryOffscreenImage = new BufferedImage(imgDisplay.getWidth(), imgDisplay.getHeight(),
                     BufferedImage.TYPE_3BYTE_BGR);
+
             Vector3D currentRotation = null;
-            if (this.cameraPlane != null) {
-                currentRotation = this.cameraPlane.getRotation();
+            if (currentCameraPlane != null) {
+                currentRotation = currentCameraPlane.getRotation();
+                Logging.error(currentRotation.toString());
             }
-            this.cameraPlane = new CameraPlane(imgDisplay.getWidth(), imgDisplay.getHeight());
+            final CameraPlane temporaryCameraPlane = new CameraPlane(imgDisplay.getWidth(), imgDisplay.getHeight());
             if (currentRotation != null) {
-                this.cameraPlane.setRotation(currentRotation);
+                temporaryCameraPlane.setRotation(currentRotation);
+            }
+            synchronized (this) {
+                this.cameraPlane = temporaryCameraPlane;
+                this.offscreenImage = temporaryOffscreenImage;
             }
-            GuiHelper.runInEDT(imgDisplay::invalidate);
+            //GuiHelper.runInEDT(imgDisplay::revalidate);
         }
     }
 
diff --git a/src/org/openstreetmap/josm/gui/util/imagery/CameraPlane.java b/src/org/openstreetmap/josm/gui/util/imagery/CameraPlane.java
index 1a4b491a0d..e63c0526d1 100644
--- a/src/org/openstreetmap/josm/gui/util/imagery/CameraPlane.java
+++ b/src/org/openstreetmap/josm/gui/util/imagery/CameraPlane.java
@@ -10,8 +10,6 @@ import java.awt.image.DataBufferInt;
 import java.util.stream.IntStream;
 import javax.annotation.Nullable;
 
-import org.openstreetmap.josm.tools.Logging;
-
 /**
  * The plane that the camera appears on and rotates around.
  */
@@ -155,17 +153,19 @@ public class CameraPlane {
      * @param to The new point
      */
     public void setRotationFromDelta(final Point from, final Point to) {
-        try {
-            Vector3D f1 = vectors[from.x][from.y];
-            Vector3D t1 = vectors[to.x][to.y];
-            double deltaPolarAngle = f1.getPolarAngle() - t1.getPolarAngle();
-            double deltaAzimuthalAngle = t1.getAzimuthalAngle() - f1.getAzimuthalAngle();
-            double polarAngle = this.rotation.getPolarAngle() + deltaPolarAngle;
-            double azimuthalAngle = this.rotation.getAzimuthalAngle() + deltaAzimuthalAngle;
-            this.setRotation(azimuthalAngle, polarAngle);
-        } catch (ArrayIndexOutOfBoundsException e) {
-            Logging.error(e);
+        // Bound check (bounds are essentially the image viewer component)
+        if (from.x < 0 || from.y < 0 || to.x < 0 || to.y < 0
+            || from.x > this.vectors.length || from.y > this.vectors[0].length
+            || to.x > this.vectors.length || to.y > this.vectors[0].length) {
+            return;
         }
+        Vector3D f1 = this.vectors[from.x][from.y];
+        Vector3D t1 = this.vectors[to.x][to.y];
+        double deltaPolarAngle = f1.getPolarAngle() - t1.getPolarAngle();
+        double deltaAzimuthalAngle = t1.getAzimuthalAngle() - f1.getAzimuthalAngle();
+        double polarAngle = this.rotation.getPolarAngle() + deltaPolarAngle;
+        double azimuthalAngle = this.rotation.getAzimuthalAngle() + deltaAzimuthalAngle;
+        this.setRotation(azimuthalAngle, polarAngle);
     }
 
     /**
@@ -183,7 +183,7 @@ public class CameraPlane {
 
     synchronized void setRotation(double azimuthalAngle, double polarAngle) {
         // Note: Something, somewhere, is switching the two.
-        // So the bounds are flipped. FIXME sometime
+        // FIXME: Figure out what is switching them and why
         // Prevent us from going much outside 2pi
         if (polarAngle < 0) {
             polarAngle = polarAngle + TWO_PI;
@@ -203,13 +203,55 @@ public class CameraPlane {
         return rotate(vec, 1);
     }
 
+    /**
+     * Rotate a vector using the current rotation
+     * @param vec The vector to rotate
+     * @param rotationFactor Used to determine if using left hand rule or right hand rule (1 for RHR)
+     * @return A rotated vector
+     */
     private Vector3D rotate(final Vector3D vec, final int rotationFactor) {
-        double vecX, vecY, vecZ;
-        // Rotate around z axis first
-        vecZ = vec.getZ() * this.rotation.getAzimuthalAngleCos() - vec.getY() * this.rotation.getAzimuthalAngleSin();
-        vecY = vec.getZ() * this.rotation.getAzimuthalAngleSin() + vec.getY() * this.rotation.getAzimuthalAngleCos();
-        vecX = vecZ * this.rotation.getPolarAngleSin() * rotationFactor + vec.getX() * this.rotation.getPolarAngleCos();
-        vecZ = vecZ * this.rotation.getPolarAngleCos() - vec.getX() * this.rotation.getPolarAngleSin() * rotationFactor;
+        // @formatting:off
+        /* Full rotation matrix for a yaw-pitch-roll
+         * yaw = alpha, pitch = beta, roll = gamma (typical representations)
+         * [cos(alpha), -sin(alpha), 0 ]   [cos(beta), 0, sin(beta) ]   [1,     0     ,     0      ]   [x]   [x1]
+         * |sin(alpha), cos(alpha), 0  | . |0        , 1, 0         | . |0, cos(gamma), -sin(gamma)| . |y| = |y1|
+         * [0         ,       0    , 1 ]   [-sin(beta), 0, cos(beta)]   [0, sin(gamma), cos(gamma) ]   [z]   [z1]
+         * which becomes
+         * x1 = y(cos(alpha)sin(beta)sin(gamma) - sin(alpha)cos(gamma)) + z(cos(alpha)sin(beta)cos(gamma) + sin(alpha)sin(gamma)) + x cos(alpha)cos(beta)
+         * y1 = y(sin(alpha)sin(beta)sin(gamma) + cos(alpha)cos(gamma)) + z(sin(alpha)sin(beta)cos(gamma) - cos(alpha)sin(gamma)) + x sin(alpha)cos(beta)
+         * z1 = y cos(beta)sin(gamma) + z cos(beta)cos(gamma) - x sin(beta)
+         */
+        // @formatting:on
+        double vecX;
+        double vecY;
+        double vecZ;
+        // We only do pitch/roll (we specifically do not do roll -- this would lead to tilting the image)
+        // So yaw (alpha) -> azimuthalAngle, pitch (beta) -> polarAngle, roll (gamma) -> 0 (sin(gamma) -> 0, cos(gamma) -> 1)
+        if (true) {
+            // gamma is set here just to make it slightly easier to tilt images in the future -- we just have to set the gamma somewhere else.
+            final int gamma = 0;
+            final double sinGamma = Math.sin(gamma);
+            final double cosGamma = Math.cos(gamma);
+            final double cosAlpha = this.rotation.getAzimuthalAngleCos();
+            final double sinAlpha = this.rotation.getAzimuthalAngleSin();
+            final double cosBeta = this.rotation.getPolarAngleCos();
+            final double sinBeta = this.rotation.getPolarAngleSin();
+            final double x = vec.getX();
+            final double y = vec.getY();
+            final double z = vec.getZ();
+            vecX = y * (cosAlpha * sinBeta * sinGamma - sinAlpha * cosGamma)
+                    + z * (cosAlpha * sinBeta * cosGamma + sinAlpha * sinGamma) + x * cosAlpha * cosBeta;
+            vecY = y * (sinAlpha * sinBeta * sinGamma + cosAlpha * cosGamma)
+                    + z * (sinAlpha * sinBeta * cosGamma - cosAlpha * sinGamma) + x * sinAlpha * cosBeta;
+            vecZ = y * cosBeta * sinGamma + z * cosBeta * cosGamma - x * sinBeta;
+        } else {
+            // This is the original Mapillary code
+            // Rotate around z axis first
+            vecZ = vec.getZ() * this.rotation.getAzimuthalAngleCos() - vec.getY() * this.rotation.getAzimuthalAngleSin();
+            vecY = vec.getZ() * this.rotation.getAzimuthalAngleSin() + vec.getY() * this.rotation.getAzimuthalAngleCos();
+            vecX = vecZ * this.rotation.getPolarAngleSin() * rotationFactor + vec.getX() * this.rotation.getPolarAngleCos();
+            vecZ = vecZ * this.rotation.getPolarAngleCos() - vec.getX() * this.rotation.getPolarAngleSin() * rotationFactor;
+        }
         return new Vector3D(vecX, vecY, vecZ);
     }
 
