Index: trunk/src/org/openstreetmap/josm/gui/layer/GpxLayer.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/layer/GpxLayer.java	(revision 12156)
+++ trunk/src/org/openstreetmap/josm/gui/layer/GpxLayer.java	(revision 12157)
@@ -10,8 +10,5 @@
 import java.text.DateFormat;
 import java.util.Arrays;
-import java.util.Collection;
 import java.util.Date;
-import java.util.LinkedList;
-import java.util.List;
 
 import javax.swing.Action;
@@ -20,5 +17,4 @@
 import javax.swing.SwingUtilities;
 
-import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.actions.RenameLayerAction;
 import org.openstreetmap.josm.actions.SaveActionBase;
@@ -28,5 +24,4 @@
 import org.openstreetmap.josm.data.gpx.GpxData;
 import org.openstreetmap.josm.data.gpx.GpxTrack;
-import org.openstreetmap.josm.data.gpx.WayPoint;
 import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
 import org.openstreetmap.josm.data.preferences.ColorProperty;
@@ -66,6 +61,4 @@
     public boolean[] trackVisibility = new boolean[0];
 
-    private final GpxDrawHelper drawHelper;
-
     /**
      * Constructs a new {@code GpxLayer} without name.
@@ -95,7 +88,6 @@
         data = d;
         data.addWeakChangeListener(e -> this.invalidate());
-        drawHelper = new GpxDrawHelper(data);
-        SystemOfMeasurement.addSoMChangeListener(drawHelper);
-        ensureTrackVisibilityLength();
+        trackVisibility = new boolean[data.getTracks().size()];
+        Arrays.fill(trackVisibility, true);
         setName(name);
         isLocalFile = isLocal;
@@ -280,48 +272,5 @@
             throw new IllegalArgumentException("not a GpxLayer: " + from);
         data.mergeFrom(((GpxLayer) from).data);
-        drawHelper.dataChanged();
-    }
-
-    @Override
-    public void paint(Graphics2D g, MapView mv, Bounds box) {
-        List<WayPoint> visibleSegments = listVisibleSegments(box);
-        if (!visibleSegments.isEmpty()) {
-            drawHelper.readPreferences(getName());
-            drawHelper.drawAll(g, mv, visibleSegments);
-            if (Main.getLayerManager().getActiveLayer() == this) {
-                drawHelper.drawColorBar(g, mv);
-            }
-        }
-    }
-
-    private List<WayPoint> listVisibleSegments(Bounds box) {
-        WayPoint last = null;
-        LinkedList<WayPoint> visibleSegments = new LinkedList<>();
-
-        ensureTrackVisibilityLength();
-        for (Collection<WayPoint> segment : data.getLinesIterable(trackVisibility)) {
-
-            for (WayPoint pt : segment) {
-                Bounds b = new Bounds(pt.getCoor());
-                if (pt.drawLine && last != null) {
-                    b.extend(last.getCoor());
-                }
-                if (b.intersects(box)) {
-                    if (last != null && (visibleSegments.isEmpty()
-                            || visibleSegments.getLast() != last)) {
-                        if (last.drawLine) {
-                            WayPoint l = new WayPoint(last);
-                            l.drawLine = false;
-                            visibleSegments.add(l);
-                        } else {
-                            visibleSegments.add(last);
-                        }
-                    }
-                    visibleSegments.add(pt);
-                }
-                last = pt;
-            }
-        }
-        return visibleSegments;
+        invalidate();
     }
 
@@ -339,19 +288,4 @@
     public void setAssociatedFile(File file) {
         data.storageFile = file;
-    }
-
-    /** ensures the trackVisibility array has the correct length without losing data.
-     * additional entries are initialized to true;
-     */
-    private void ensureTrackVisibilityLength() {
-        final int l = data.getTracks().size();
-        if (l == trackVisibility.length)
-            return;
-        final int m = Math.min(l, trackVisibility.length);
-        trackVisibility = Arrays.copyOf(trackVisibility, l);
-        for (int i = m; i < l; i++) {
-            trackVisibility[i] = true;
-        }
-        invalidate();
     }
 
@@ -383,7 +317,11 @@
 
     @Override
-    public synchronized void destroy() {
-        super.destroy();
-        SystemOfMeasurement.removeSoMChangeListener(drawHelper);
+    public void paint(Graphics2D g, MapView mv, Bounds bbox) {
+        // unused - we use a painter so this is not called.
+    }
+
+    @Override
+    protected LayerPainter createMapViewPainter(MapViewEvent event) {
+        return new GpxDrawHelper(this);
     }
 }
Index: trunk/src/org/openstreetmap/josm/gui/layer/gpx/GpxDrawHelper.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/layer/gpx/GpxDrawHelper.java	(revision 12156)
+++ trunk/src/org/openstreetmap/josm/gui/layer/gpx/GpxDrawHelper.java	(revision 12157)
@@ -27,4 +27,5 @@
 import java.util.Collections;
 import java.util.Date;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Random;
@@ -33,4 +34,5 @@
 
 import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.Bounds;
 import org.openstreetmap.josm.data.SystemOfMeasurement;
 import org.openstreetmap.josm.data.SystemOfMeasurement.SoMChangeListener;
@@ -42,4 +44,10 @@
 import org.openstreetmap.josm.gui.MapView;
 import org.openstreetmap.josm.gui.MapViewState;
+import org.openstreetmap.josm.gui.layer.GpxLayer;
+import org.openstreetmap.josm.gui.layer.MapViewGraphics;
+import org.openstreetmap.josm.gui.layer.MapViewPaintable;
+import org.openstreetmap.josm.gui.layer.MapViewPaintable.MapViewEvent;
+import org.openstreetmap.josm.gui.layer.MapViewPaintable.PaintableInvalidationEvent;
+import org.openstreetmap.josm.gui.layer.MapViewPaintable.PaintableInvalidationListener;
 import org.openstreetmap.josm.io.CachedFile;
 import org.openstreetmap.josm.tools.ColorScale;
@@ -51,5 +59,5 @@
  * @since 7319
  */
-public class GpxDrawHelper implements SoMChangeListener {
+public class GpxDrawHelper implements SoMChangeListener, MapViewPaintable.LayerPainter, PaintableInvalidationListener {
 
     /**
@@ -60,4 +68,5 @@
 
     private final GpxData data;
+    private final GpxLayer layer;
 
     // draw lines between points belonging to different segments
@@ -167,4 +176,8 @@
     private Color[] heatMapLutColor = createColorLut(0, Color.BLACK, Color.WHITE);
 
+    // The heat map was invalidated since the last draw.
+    private boolean gpxLayerInvalidated;
+
+
     private void setupColors() {
         hdopAlpha = Main.pref.getInteger("hdop.color.alpha", -1);
@@ -204,9 +217,13 @@
     /**
      * Constructs a new {@code GpxDrawHelper}.
-     * @param gpxData GPX data
-     * @since 11713
-     */
-    public GpxDrawHelper(GpxData gpxData) {
-        data = gpxData;
+     * @param gpxLayer The layer to draw
+     * @since 12157
+     */
+    public GpxDrawHelper(GpxLayer gpxLayer) {
+        layer = gpxLayer;
+        data = gpxLayer.data;
+
+        layer.addInvalidationListener(this);
+        SystemOfMeasurement.addSoMChangeListener(this);
         setupColors();
     }
@@ -301,4 +318,62 @@
 
         largesize += lineWidth;
+    }
+
+    @Override
+    public void paint(MapViewGraphics graphics) {
+        List<WayPoint> visibleSegments = listVisibleSegments(graphics.getClipBounds().getLatLonBoundsBox());
+        if (!visibleSegments.isEmpty()) {
+            readPreferences(layer.getName());
+            drawAll(graphics.getDefaultGraphics(), graphics.getMapView(), visibleSegments);
+            if (graphics.getMapView().getLayerManager().getActiveLayer() == layer) {
+                drawColorBar(graphics.getDefaultGraphics(), graphics.getMapView());
+            }
+        }
+    }
+
+    private List<WayPoint> listVisibleSegments(Bounds box) {
+        WayPoint last = null;
+        LinkedList<WayPoint> visibleSegments = new LinkedList<>();
+
+        ensureTrackVisibilityLength();
+        for (Collection<WayPoint> segment : data.getLinesIterable(layer.trackVisibility)) {
+
+            for (WayPoint pt : segment) {
+                Bounds b = new Bounds(pt.getCoor());
+                if (pt.drawLine && last != null) {
+                    b.extend(last.getCoor());
+                }
+                if (b.intersects(box)) {
+                    if (last != null && (visibleSegments.isEmpty()
+                            || visibleSegments.getLast() != last)) {
+                        if (last.drawLine) {
+                            WayPoint l = new WayPoint(last);
+                            l.drawLine = false;
+                            visibleSegments.add(l);
+                        } else {
+                            visibleSegments.add(last);
+                        }
+                    }
+                    visibleSegments.add(pt);
+                }
+                last = pt;
+            }
+        }
+        return visibleSegments;
+    }
+
+    /** ensures the trackVisibility array has the correct length without losing data.
+     * TODO: Make this nicer by syncing the trackVisibility automatically.
+     * additional entries are initialized to true;
+     */
+    private void ensureTrackVisibilityLength() {
+        final int l = data.getTracks().size();
+        if (l == layer.trackVisibility.length)
+            return;
+        final int m = Math.min(l, layer.trackVisibility.length);
+        layer.trackVisibility = Arrays.copyOf(layer.trackVisibility, l);
+        for (int i = m; i < l; i++) {
+            layer.trackVisibility[i] = true;
+        }
     }
 
@@ -1203,6 +1278,7 @@
 
         // recalculation of image needed
-        final boolean imageRecalc = !mapViewState.equalsInWindow(heatMapMapViewState) ||
-                                    heatMapCacheLineWith != globalLineWidth;
+        final boolean imageRecalc = !mapViewState.equalsInWindow(heatMapMapViewState)
+                || gpxLayerInvalidated
+                || heatMapCacheLineWith != globalLineWidth;
 
         // need re-generation of gray image ?
@@ -1228,4 +1304,5 @@
             heatMapMapViewState = mapViewState;
             heatMapCacheLineWith = globalLineWidth;
+            gpxLayerInvalidated = false;
         }
 
@@ -1402,3 +1479,14 @@
         }
     }
+
+    @Override
+    public void paintableInvalidated(PaintableInvalidationEvent event) {
+        gpxLayerInvalidated = true;
+    }
+
+    @Override
+    public void detachFromMapView(MapViewEvent event) {
+        SystemOfMeasurement.removeSoMChangeListener(this);
+        layer.removeInvalidationListener(this);
+    }
 }
Index: trunk/test/unit/org/openstreetmap/josm/gui/layer/gpx/GpxDrawHelperTest.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/gui/layer/gpx/GpxDrawHelperTest.java	(revision 12156)
+++ trunk/test/unit/org/openstreetmap/josm/gui/layer/gpx/GpxDrawHelperTest.java	(revision 12157)
@@ -15,4 +15,5 @@
 import org.openstreetmap.josm.TestUtils;
 import org.openstreetmap.josm.data.gpx.GpxData;
+import org.openstreetmap.josm.gui.layer.GpxLayer;
 import org.openstreetmap.josm.io.GpxReaderTest;
 import org.openstreetmap.josm.tools.ColorHelper;
@@ -123,5 +124,6 @@
     static List<String> calculateColors(String fileName, String layerName, int n) throws IOException, SAXException {
         final GpxData data = GpxReaderTest.parseGpxData(fileName);
-        final GpxDrawHelper gdh = new GpxDrawHelper(data);
+        final GpxLayer layer = new GpxLayer(data);
+        final GpxDrawHelper gdh = new GpxDrawHelper(layer);
         gdh.readPreferences(layerName);
         gdh.calculateColors();
