Index: trunk/src/org/openstreetmap/josm/data/gpx/GpxData.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/gpx/GpxData.java	(revision 7318)
+++ trunk/src/org/openstreetmap/josm/data/gpx/GpxData.java	(revision 7319)
@@ -4,4 +4,5 @@
 import java.io.File;
 import java.util.Collection;
+import java.util.Date;
 import java.util.Iterator;
 import java.util.LinkedList;
@@ -82,4 +83,5 @@
      *
      * FIXME might perhaps use visitor pattern?
+     * @return the bounds
      */
     public Bounds recalculateBounds() {
@@ -116,4 +118,5 @@
     /**
      * calculates the sum of the lengths of all track segments
+     * @return the length in meters
      */
     public double length(){
@@ -125,4 +128,54 @@
 
         return result;
+    }
+    
+    /**
+     * returns minimum and maximum timestamps in the track
+     * @param trk track to analyze
+     * @return  minimum and maximum dates in array of 2 elements
+     */
+    public static Date[] getMinMaxTimeForTrack(GpxTrack trk) {
+        WayPoint earliest = null, latest = null;
+
+        for (GpxTrackSegment seg : trk.getSegments()) {
+            for (WayPoint pnt : seg.getWayPoints()) {
+                if (latest == null) {
+                    latest = earliest = pnt;
+                } else {
+                    if (pnt.compareTo(earliest) < 0) {
+                        earliest = pnt;
+                    } else {
+                        latest = pnt;
+                    }
+                }
+            }
+        }
+        if (earliest==null || latest==null) return null;
+        return new Date[]{earliest.getTime(), latest.getTime()};
+    }
+
+    /**
+    * Returns minimum and maximum timestamps for all tracks
+    * Warning: there are lot of track with broken timestamps,
+    * so we just ingore points from future and from year before 1970 in this method
+    * works correctly @since 5815
+     * @return minimum and maximum dates in array of 2 elements
+    */
+    public Date[] getMinMaxTimeForAllTracks() {
+        double min=1e100, max=-1e100, t;
+        double now = System.currentTimeMillis()/1000.0;
+        for (GpxTrack trk: tracks) {
+            for (GpxTrackSegment seg : trk.getSegments()) {
+                for (WayPoint pnt : seg.getWayPoints()) {
+                    t = pnt.time;
+                    if (t>0 && t<=now) {
+                        if (t>max) max=t;
+                        if (t<min) min=t;
+                    }
+                }
+            }
+        }
+        if (min==1e100 || max==-1e100) return null;
+        return new Date[]{new Date((long) (min * 1000)), new Date((long) (max * 1000)), };
     }
 
@@ -258,4 +311,31 @@
     }
 
+    public void resetEastNorthCache() {
+        if (waypoints != null) {
+            for (WayPoint wp : waypoints){
+                wp.invalidateEastNorthCache();
+            }
+        }
+        if (tracks != null){
+            for (GpxTrack track: tracks) {
+                for (GpxTrackSegment segment: track.getSegments()) {
+                    for (WayPoint wp: segment.getWayPoints()) {
+                        wp.invalidateEastNorthCache();
+                    }
+                }
+            }
+        }
+        if (routes != null) {
+            for (GpxRoute route: routes) {
+                if (route.routePoints == null) {
+                    continue;
+                }
+                for (WayPoint wp: route.routePoints) {
+                    wp.invalidateEastNorthCache();
+                }
+            }
+        }
+    }
+
     /**
      * Iterates over all track segments and then over all routes.
@@ -266,8 +346,8 @@
         private int idxTracks;
         private Iterator<GpxTrackSegment> itTrackSegments;
-        private Iterator<GpxRoute> itRoutes;
+        private final Iterator<GpxRoute> itRoutes;
 
         private Collection<WayPoint> next;
-        private boolean[] trackVisibility;
+        private final boolean[] trackVisibility;
 
         public LinesIterator(GpxData data, boolean[] trackVisibility) {
Index: trunk/src/org/openstreetmap/josm/data/gpx/WayPoint.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/gpx/WayPoint.java	(revision 7318)
+++ trunk/src/org/openstreetmap/josm/data/gpx/WayPoint.java	(revision 7319)
@@ -25,5 +25,4 @@
     public double time;
     public Color customColoring;
-    public Color customColoringTransparent;
     public boolean drawLine;
     public int dir;
@@ -37,5 +36,4 @@
         time = p.time;
         customColoring = p.customColoring;
-        customColoringTransparent = p.customColoringTransparent;
         drawLine = p.drawLine;
         dir = p.dir;
Index: trunk/src/org/openstreetmap/josm/gui/layer/GpxLayer.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/layer/GpxLayer.java	(revision 7318)
+++ trunk/src/org/openstreetmap/josm/gui/layer/GpxLayer.java	(revision 7319)
@@ -3,15 +3,10 @@
 package org.openstreetmap.josm.gui.layer;
 
-import static org.openstreetmap.josm.tools.I18n.marktr;
 import static org.openstreetmap.josm.tools.I18n.tr;
 import static org.openstreetmap.josm.tools.I18n.trn;
 
-import java.awt.BasicStroke;
 import java.awt.Color;
 import java.awt.Dimension;
 import java.awt.Graphics2D;
-import java.awt.Point;
-import java.awt.RenderingHints;
-import java.awt.Stroke;
 import java.io.File;
 import java.text.DateFormat;
@@ -27,10 +22,9 @@
 import javax.swing.JScrollPane;
 import javax.swing.SwingUtilities;
-
 import org.openstreetmap.josm.Main;
+
 import org.openstreetmap.josm.actions.RenameLayerAction;
 import org.openstreetmap.josm.actions.SaveActionBase;
 import org.openstreetmap.josm.data.Bounds;
-import org.openstreetmap.josm.data.coor.LatLon;
 import org.openstreetmap.josm.data.gpx.GpxConstants;
 import org.openstreetmap.josm.data.gpx.GpxData;
@@ -50,4 +44,5 @@
 import org.openstreetmap.josm.gui.layer.gpx.DownloadAlongTrackAction;
 import org.openstreetmap.josm.gui.layer.gpx.DownloadWmsAlongTrackAction;
+import org.openstreetmap.josm.gui.layer.gpx.GpxDrawHelper;
 import org.openstreetmap.josm.gui.layer.gpx.ImportAudioAction;
 import org.openstreetmap.josm.gui.layer.gpx.ImportImagesAction;
@@ -56,5 +51,4 @@
 import org.openstreetmap.josm.io.GpxImporter;
 import org.openstreetmap.josm.tools.ImageProvider;
-import org.openstreetmap.josm.tools.Utils;
 import org.openstreetmap.josm.tools.date.DateUtils;
 
@@ -62,11 +56,4 @@
 
     public GpxData data;
-    protected static final double PHI = Math.toRadians(15);
-    private boolean computeCacheInSync;
-    private int computeCacheMaxLineLengthUsed;
-    private Color computeCacheColorUsed;
-    private boolean computeCacheColorDynamic;
-    private colorModes computeCacheColored;
-    private int computeCacheColorTracksTune;
     private boolean isLocalFile;
     // used by ChooseTrackVisibilityAction to determine which tracks to show/hide
@@ -76,8 +63,10 @@
     private int lastUpdateCount;
 
+    private final GpxDrawHelper drawHelper;
+    
     public GpxLayer(GpxData d) {
         super((String) d.attr.get("name"));
         data = d;
-        computeCacheInSync = false;
+        drawHelper = new GpxDrawHelper(data);
         ensureTrackVisibilityLength();
     }
@@ -94,52 +83,9 @@
     }
 
-    /**
-     * returns minimum and maximum timestamps in the track
-     */
-    public static Date[] getMinMaxTimeForTrack(GpxTrack trk) {
-        WayPoint earliest = null, latest = null;
-
-        for (GpxTrackSegment seg : trk.getSegments()) {
-            for (WayPoint pnt : seg.getWayPoints()) {
-                if (latest == null) {
-                    latest = earliest = pnt;
-                } else {
-                    if (pnt.compareTo(earliest) < 0) {
-                        earliest = pnt;
-                    } else {
-                        latest = pnt;
-                    }
-                }
-            }
-        }
-        if (earliest==null || latest==null) return null;
-        return new Date[]{earliest.getTime(), latest.getTime()};
-    }
-
-    /**
-    * Returns minimum and maximum timestamps for all tracks
-    * Warning: there are lot of track with broken timestamps,
-    * so we just ingore points from future and from year before 1970 in this method
-    * works correctly @since 5815
-    */
-    public Date[] getMinMaxTimeForAllTracks() {
-        double min=1e100, max=-1e100, t;
-        double now = System.currentTimeMillis()/1000.0;
-        for (GpxTrack trk: data.tracks) {
-            for (GpxTrackSegment seg : trk.getSegments()) {
-                for (WayPoint pnt : seg.getWayPoints()) {
-                    t = pnt.time;
-                    if (t>0 && t<=now) {
-                        if (t>max) max=t;
-                        if (t<min) min=t;
-                    }
-                }
-            }
-        }
-        if (min==1e100 || max==-1e100) return null;
-        return new Date[]{new Date((long) (min * 1000)), new Date((long) (max * 1000)), };
-    }
-
-
+    @Override
+    public Color getColor(boolean ignoreCustom) {
+        return drawHelper.getColor(getName(), ignoreCustom);
+    }
+    
     /**
      * Returns a human readable string that shows the timespan of the given track
@@ -148,5 +94,5 @@
      */
     public static String getTimespanForTrack(GpxTrack trk) {
-        Date[] bounds = getMinMaxTimeForTrack(trk);
+        Date[] bounds = GpxData.getMinMaxTimeForTrack(trk);
         String ts = "";
         if (bounds != null) {
@@ -238,26 +184,4 @@
     public boolean isInfoResizable() {
         return true;
-    }
-
-    @Override
-    public Color getColor(boolean ignoreCustom) {
-        Color c = Main.pref.getColor(marktr("gps point"), "layer " + getName(), Color.gray);
-
-        return ignoreCustom || getColorMode() == colorModes.none ? c : null;
-    }
-
-    public colorModes getColorMode() {
-        try {
-            int i=Main.pref.getInteger("draw.rawgps.colors", "layer " + getName(), 0);
-            return colorModes.values()[i];
-        } catch (Exception e) {
-            Main.warn(e);
-        }
-        return colorModes.none;
-    }
-
-    /* for preferences */
-    public static Color getGenericColor() {
-        return Main.pref.getColor(marktr("gps point"), Color.gray);
     }
 
@@ -337,5 +261,5 @@
         long to = toDate.getTime();
         for (GpxTrack trk : data.tracks) {
-            Date[] t = GpxLayer.getMinMaxTimeForTrack(trk);
+            Date[] t = GpxData.getMinMaxTimeForTrack(trk);
 
             if (t==null) continue;
@@ -349,73 +273,5 @@
     public void mergeFrom(Layer from) {
         data.mergeFrom(((GpxLayer) from).data);
-        computeCacheInSync = false;
-    }
-
-    private static final Color[] colors = new Color[256];
-    static {
-        for (int i = 0; i < colors.length; i++) {
-            colors[i] = Color.getHSBColor(i / 300.0f, 1, 1);
-        }
-    }
-    /** Colors (with custom alpha channel, if given) for HDOP painting. */
-    private final Color[] hdopColors;
-    private final int hdopAlpha = Main.pref.getInteger("hdop.color.alpha", -1);
-    {
-        if (hdopAlpha >= 0) {
-            hdopColors = new Color[256];
-            for (int i = 0; i < hdopColors.length; i++) {
-                hdopColors[i] = new Color((colors[i].getRGB() & 0xFFFFFF) | ((hdopAlpha & 0xFF) << 24), true);
-            }
-        } else {
-            hdopColors = colors;
-        }
-    }
-
-    private static final Color[] colors_cyclic = new Color[256];
-    static {
-        for (int i = 0; i < colors_cyclic.length; i++) {
-            //                    red   yellow  green   blue    red
-            int[] h = new int[] { 0,    59,     127,    244,    360};
-            int[] s = new int[] { 100,  84,     99,     100 };
-            int[] b = new int[] { 90,   93,     74,     83 };
-
-            float angle = 4 - i / 256f * 4;
-            int quadrant = (int) angle;
-            angle -= quadrant;
-            quadrant = Utils.mod(quadrant+1, 4);
-
-            float vh = h[quadrant] * w(angle) + h[quadrant+1] * (1 - w(angle));
-            float vs = s[quadrant] * w(angle) + s[Utils.mod(quadrant+1, 4)] * (1 - w(angle));
-            float vb = b[quadrant] * w(angle) + b[Utils.mod(quadrant+1, 4)] * (1 - w(angle));
-
-            colors_cyclic[i] = Color.getHSBColor(vh/360f, vs/100f, vb/100f);
-        }
-    }
-
-    /**
-     * transition function:
-     *  w(0)=1, w(1)=0, 0&lt;=w(x)&lt;=1
-     * @param x number: 0&lt;=x&lt;=1
-     * @return the weighted value
-     */
-    private static float w(float x) {
-        if (x < 0.5)
-            return 1 - 2*x*x;
-        else
-            return 2*(1-x)*(1-x);
-    }
-
-    // lookup array to draw arrows without doing any math
-    private static final int ll0 = 9;
-    private static final int sl4 = 5;
-    private static final int sl9 = 3;
-    private static final int[][] dir = { { +sl4, +ll0, +ll0, +sl4 }, { -sl9, +ll0, +sl9, +ll0 }, { -ll0, +sl4, -sl4, +ll0 },
-        { -ll0, -sl9, -ll0, +sl9 }, { -sl4, -ll0, -ll0, -sl4 }, { +sl9, -ll0, -sl9, -ll0 },
-        { +ll0, -sl4, +sl4, -ll0 }, { +ll0, +sl9, +ll0, -sl9 }, { +sl4, +ll0, +ll0, +sl4 },
-        { -sl9, +ll0, +sl9, +ll0 }, { -ll0, +sl4, -sl4, +ll0 }, { -ll0, -sl9, -ll0, +sl9 } };
-
-    // the different color modes
-    enum colorModes {
-        none, velocity, dilution, direction, time
+        drawHelper.dataChanged();
     }
 
@@ -426,209 +282,22 @@
         lastTracks.addAll(data.tracks);
 
-        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
-                Main.pref.getBoolean("mappaint.gpx.use-antialiasing", false) ?
-                        RenderingHints.VALUE_ANTIALIAS_ON : RenderingHints.VALUE_ANTIALIAS_OFF);
-
-        /****************************************************************
-         ********** STEP 1 - GET CONFIG VALUES **************************
-         ****************************************************************/
-        Color neutralColor = getColor(true);
-        String spec="layer "+getName();
-
-        // also draw lines between points belonging to different segments
-        boolean forceLines = Main.pref.getBoolean("draw.rawgps.lines.force", spec, false);
-        // draw direction arrows on the lines
-        boolean direction = Main.pref.getBoolean("draw.rawgps.direction", spec, false);
-        // don't draw lines if longer than x meters
-        int lineWidth = Main.pref.getInteger("draw.rawgps.linewidth", spec, 0);
-
-        int maxLineLength;
-        boolean lines;
-        if (!this.data.fromServer) {
-            maxLineLength = Main.pref.getInteger("draw.rawgps.max-line-length.local", spec, -1);
-            lines = Main.pref.getBoolean("draw.rawgps.lines.local", spec, true);
-        } else {
-            maxLineLength = Main.pref.getInteger("draw.rawgps.max-line-length", spec, 200);
-            lines = Main.pref.getBoolean("draw.rawgps.lines", spec, true);
-        }
-        // paint large dots for points
-        boolean large = Main.pref.getBoolean("draw.rawgps.large", spec, false);
-        int largesize = Main.pref.getInteger("draw.rawgps.large.size", spec, 3);
-        boolean hdopcircle = Main.pref.getBoolean("draw.rawgps.hdopcircle", spec, false);
-        // color the lines
-        colorModes colored = getColorMode();
-        // paint direction arrow with alternate math. may be faster
-        boolean alternatedirection = Main.pref.getBoolean("draw.rawgps.alternatedirection", spec, false);
-        // don't draw arrows nearer to each other than this
-        int delta = Main.pref.getInteger("draw.rawgps.min-arrow-distance", spec, 40);
-        // allows to tweak line coloring for different speed levels.
-        int colorTracksTune = Main.pref.getInteger("draw.rawgps.colorTracksTune", spec, 45);
-        boolean colorModeDynamic = Main.pref.getBoolean("draw.rawgps.colors.dynamic", spec, false);
-        int hdopfactor = Main.pref.getInteger("hdop.factor", 25);
-
-        int largePointAlpha = Main.pref.getInteger("draw.rawgps.large.alpha", -1) & 0xFF;
-
-        Stroke storedStroke = g.getStroke();
-        if(lineWidth != 0)
-        {
-            g.setStroke(new BasicStroke(lineWidth,BasicStroke.CAP_ROUND,BasicStroke.JOIN_ROUND));
-            largesize += lineWidth;
-        }
-
-        /****************************************************************
-         ********** STEP 2a - CHECK CACHE VALIDITY **********************
-         ****************************************************************/
-        if ((computeCacheMaxLineLengthUsed != maxLineLength) || (!neutralColor.equals(computeCacheColorUsed))
-                || (computeCacheColored != colored) || (computeCacheColorTracksTune != colorTracksTune)
-                || (computeCacheColorDynamic != colorModeDynamic)) {
-            computeCacheMaxLineLengthUsed = maxLineLength;
-            computeCacheInSync = false;
-            computeCacheColorUsed = neutralColor;
-            computeCacheColored = colored;
-            computeCacheColorTracksTune = colorTracksTune;
-            computeCacheColorDynamic = colorModeDynamic;
-        }
-
-        /****************************************************************
-         ********** STEP 2b - RE-COMPUTE CACHE DATA *********************
-         ****************************************************************/
-        if (!computeCacheInSync) { // don't compute if the cache is good
-            double minval = +1e10;
-            double maxval = -1e10;
-            WayPoint oldWp = null;
-            if (colorModeDynamic) {
-                if (colored == colorModes.velocity) {
-                    for (Collection<WayPoint> segment : data.getLinesIterable(null)) {
-                        if(!forceLines) {
-                            oldWp = null;
-                        }
-                        for (WayPoint trkPnt : segment) {
-                            LatLon c = trkPnt.getCoor();
-                            if (Double.isNaN(c.lat()) || Double.isNaN(c.lon())) {
-                                continue;
-                            }
-                            if (oldWp != null && trkPnt.time > oldWp.time) {
-                                double vel = c.greatCircleDistance(oldWp.getCoor())
-                                        / (trkPnt.time - oldWp.time);
-                                if(vel > maxval) {
-                                    maxval = vel;
-                                }
-                                if(vel < minval) {
-                                    minval = vel;
-                                }
-                            }
-                            oldWp = trkPnt;
-                        }
-                    }
-                } else if (colored == colorModes.dilution) {
-                    for (Collection<WayPoint> segment : data.getLinesIterable(null)) {
-                        for (WayPoint trkPnt : segment) {
-                            Object val = trkPnt.attr.get("hdop");
-                            if (val != null) {
-                                double hdop = ((Float) val).doubleValue();
-                                if(hdop > maxval) {
-                                    maxval = hdop;
-                                }
-                                if(hdop < minval) {
-                                    minval = hdop;
-                                }
-                            }
-                        }
-                    }
-                }
-                oldWp = null;
-            }
-            double now = System.currentTimeMillis()/1000.0;
-            if (colored == colorModes.time) {
-                Date[] bounds = getMinMaxTimeForAllTracks();
-                if (bounds!=null) {
-                    minval = bounds[0].getTime()/1000.0;
-                    maxval = bounds[1].getTime()/1000.0;
-                } else {
-                    minval = 0; maxval=now;
-                }
-            }
-
-            for (Collection<WayPoint> segment : data.getLinesIterable(null)) {
-                if (!forceLines) { // don't draw lines between segments, unless forced to
-                    oldWp = null;
-                }
-                for (WayPoint trkPnt : segment) {
-                    LatLon c = trkPnt.getCoor();
-                    if (Double.isNaN(c.lat()) || Double.isNaN(c.lon())) {
-                        continue;
-                    }
-                    trkPnt.customColoring = neutralColor;
-                    if (trkPnt.attr.get("hdop") != null) {
-                        if (colored == colorModes.dilution) {
-                            float hdop = ((Float) trkPnt.attr.get("hdop")).floatValue();
-                            int hdoplvl =(int) Math.round(colorModeDynamic ? ((hdop-minval)*255/(maxval-minval))
-                                    : (hdop <= 0 ? 0 : hdop * hdopfactor));
-                            // High hdop is bad, but high values in colors are green.
-                            // Therefore inverse the logic
-                            int hdopcolor = 255 - (hdoplvl > 255 ? 255 : hdoplvl);
-                            trkPnt.customColoring = colors[hdopcolor];
-                            trkPnt.customColoringTransparent = hdopColors[hdopcolor];
-                        } else {
-                            trkPnt.customColoringTransparent = new Color(
-                                    neutralColor.getRed(), neutralColor.getGreen(), neutralColor.getBlue(), hdopAlpha & 0xFF);
-                        }
-                    }
-                    if (oldWp != null) {
-                        double dist = c.greatCircleDistance(oldWp.getCoor());
-                        boolean noDraw=false;
-                        switch (colored) {
-                        case velocity:
-                            double dtime = trkPnt.time - oldWp.time;
-                            if(dtime > 0) {
-                                float vel = (float) (dist / dtime);
-                                int velColor =(int) Math.round(colorModeDynamic ? ((vel-minval)*255/(maxval-minval))
-                                        : (vel <= 0 ? 0 : vel / colorTracksTune * 255));
-                                final int vIndex = Math.max(0, Math.min(velColor, 255));
-                                trkPnt.customColoring = vIndex == 255 ? neutralColor : colors[vIndex];
-                            } else {
-                                trkPnt.customColoring = neutralColor;
-                            }
-                            break;
-                        case direction:
-                            double dirColor = oldWp.getCoor().heading(trkPnt.getCoor()) / (2.0 * Math.PI) * 256;
-                            // Bad case first
-                            if (dirColor != dirColor || dirColor < 0.0 || dirColor >= 256.0) {
-                                trkPnt.customColoring = colors_cyclic[0];
-                            } else {
-                                trkPnt.customColoring = colors_cyclic[(int) (dirColor)];
-                            }
-                            break;
-                        case time:
-                            double t=trkPnt.time;
-                            if (t > 0 && t <= now && maxval - minval > 1000) { // skip bad timestamps and very short tracks
-                                int tColor = (int) Math.round((t-minval)*255/(maxval-minval));
-                                trkPnt.customColoring = colors[tColor];
-                            } else {
-                                trkPnt.customColoring = neutralColor;
-                            }
-                            break;
-                        }
-
-                        if (!noDraw && (maxLineLength == -1 || dist <= maxLineLength)) {
-                            trkPnt.drawLine = true;
-                            trkPnt.dir = (int) oldWp.getCoor().heading(trkPnt.getCoor());
-                        } else {
-                            trkPnt.drawLine = false;
-                        }
-                    } else { // make sure we reset outdated data
-                        trkPnt.drawLine = false;
-                    }
-                    oldWp = trkPnt;
-                }
-            }
-            computeCacheInSync = true;
-        }
-
+        LinkedList<WayPoint> visibleSegments = listVisibleSegments(box);
+        if(!visibleSegments.isEmpty()) {
+            drawHelper.readPreferences(getName());
+            drawHelper.drawAll(g, mv, visibleSegments);
+            if (Main.map.mapView.getActiveLayer() == this) {
+                drawHelper.drawColorBar(g, mv);
+            }
+        }
+        
+    }
+    
+    private LinkedList<WayPoint> listVisibleSegments(Bounds box) {
+        WayPoint last = null;
         LinkedList<WayPoint> visibleSegments = new LinkedList<>();
-        WayPoint last = null;
+        
         ensureTrackVisibilityLength();
         for (Collection<WayPoint> segment : data.getLinesIterable(trackVisibility)) {
-
+            
             for(WayPoint pt : segment)
             {
@@ -655,160 +324,6 @@
             }
         }
-        if(visibleSegments.isEmpty())
-            return;
-
-        /****************************************************************
-         ********** STEP 3a - DRAW LINES ********************************
-         ****************************************************************/
-        if (lines) {
-            Point old = null;
-            for (WayPoint trkPnt : visibleSegments) {
-                LatLon c = trkPnt.getCoor();
-                if (Double.isNaN(c.lat()) || Double.isNaN(c.lon())) {
-                    continue;
-                }
-                Point screen = mv.getPoint(trkPnt.getEastNorth());
-                if (trkPnt.drawLine) {
-                    // skip points that are on the same screenposition
-                    if (old != null && ((old.x != screen.x) || (old.y != screen.y))) {
-                        g.setColor(trkPnt.customColoring);
-                        g.drawLine(old.x, old.y, screen.x, screen.y);
-                    }
-                }
-                old = screen;
-            } // end for trkpnt
-        } // end if lines
-
-        /****************************************************************
-         ********** STEP 3b - DRAW NICE ARROWS **************************
-         ****************************************************************/
-        if (lines && direction && !alternatedirection) {
-            Point old = null;
-            Point oldA = null; // last arrow painted
-            for (WayPoint trkPnt : visibleSegments) {
-                LatLon c = trkPnt.getCoor();
-                if (Double.isNaN(c.lat()) || Double.isNaN(c.lon())) {
-                    continue;
-                }
-                if (trkPnt.drawLine) {
-                    Point screen = mv.getPoint(trkPnt.getEastNorth());
-                    // skip points that are on the same screenposition
-                    if (old != null
-                            && (oldA == null || screen.x < oldA.x - delta || screen.x > oldA.x + delta
-                            || screen.y < oldA.y - delta || screen.y > oldA.y + delta)) {
-                        g.setColor(trkPnt.customColoring);
-                        double t = Math.atan2(screen.y - old.y, screen.x - old.x) + Math.PI;
-                        g.drawLine(screen.x, screen.y, (int) (screen.x + 10 * Math.cos(t - PHI)),
-                                (int) (screen.y + 10 * Math.sin(t - PHI)));
-                        g.drawLine(screen.x, screen.y, (int) (screen.x + 10 * Math.cos(t + PHI)),
-                                (int) (screen.y + 10 * Math.sin(t + PHI)));
-                        oldA = screen;
-                    }
-                    old = screen;
-                }
-            } // end for trkpnt
-        } // end if lines
-
-        /****************************************************************
-         ********** STEP 3c - DRAW FAST ARROWS **************************
-         ****************************************************************/
-        if (lines && direction && alternatedirection) {
-            Point old = null;
-            Point oldA = null; // last arrow painted
-            for (WayPoint trkPnt : visibleSegments) {
-                LatLon c = trkPnt.getCoor();
-                if (Double.isNaN(c.lat()) || Double.isNaN(c.lon())) {
-                    continue;
-                }
-                if (trkPnt.drawLine) {
-                    Point screen = mv.getPoint(trkPnt.getEastNorth());
-                    // skip points that are on the same screenposition
-                    if (old != null
-                            && (oldA == null || screen.x < oldA.x - delta || screen.x > oldA.x + delta
-                            || screen.y < oldA.y - delta || screen.y > oldA.y + delta)) {
-                        g.setColor(trkPnt.customColoring);
-                        g.drawLine(screen.x, screen.y, screen.x + dir[trkPnt.dir][0], screen.y
-                                + dir[trkPnt.dir][1]);
-                        g.drawLine(screen.x, screen.y, screen.x + dir[trkPnt.dir][2], screen.y
-                                + dir[trkPnt.dir][3]);
-                        oldA = screen;
-                    }
-                    old = screen;
-                }
-            } // end for trkpnt
-        } // end if lines
-
-        /****************************************************************
-         ********** STEP 3d - DRAW LARGE POINTS AND HDOP CIRCLE *********
-         ****************************************************************/
-        if (large || hdopcircle) {
-            final int halfSize = largesize/2;
-            g.setColor(neutralColor);
-            for (WayPoint trkPnt : visibleSegments) {
-                LatLon c = trkPnt.getCoor();
-                if (Double.isNaN(c.lat()) || Double.isNaN(c.lon())) {
-                    continue;
-                }
-                Point screen = mv.getPoint(trkPnt.getEastNorth());
-                if (!hdopcircle) {
-                    // color the large GPS points like the gps lines
-                    trkPnt.customColoringTransparent = new Color(
-                        trkPnt.customColoring.getRed(), trkPnt.customColoring.getGreen(), trkPnt.customColoring.getBlue(), largePointAlpha);
-                }
-                g.setColor(trkPnt.customColoringTransparent);
-                if (hdopcircle && trkPnt.attr.get("hdop") != null) {
-                    // hdop value
-                    float hdop = ((Float)trkPnt.attr.get("hdop")).floatValue();
-                    if (hdop < 0) {
-                        hdop = 0;
-                    }
-                    // hdop pixels
-                    int hdopp = mv.getPoint(new LatLon(trkPnt.getCoor().lat(), trkPnt.getCoor().lon() + 2*6*hdop*360/40000000)).x - screen.x;
-                    g.drawArc(screen.x-hdopp/2, screen.y-hdopp/2, hdopp, hdopp, 0, 360);
-                }
-                if (large) {
-                    g.fillRect(screen.x-halfSize, screen.y-halfSize, largesize, largesize);
-                }
-            } // end for trkpnt
-        } // end if large || hdopcircle
-
-        /****************************************************************
-         ********** STEP 3e - DRAW SMALL POINTS FOR LINES ***************
-         ****************************************************************/
-        if (!large && lines) {
-            g.setColor(neutralColor);
-            for (WayPoint trkPnt : visibleSegments) {
-                LatLon c = trkPnt.getCoor();
-                if (Double.isNaN(c.lat()) || Double.isNaN(c.lon())) {
-                    continue;
-                }
-                if (!trkPnt.drawLine) {
-                    Point screen = mv.getPoint(trkPnt.getEastNorth());
-                    g.drawRect(screen.x, screen.y, 0, 0);
-                }
-            } // end for trkpnt
-        } // end if large
-
-        /****************************************************************
-         ********** STEP 3f - DRAW SMALL POINTS INSTEAD OF LINES ********
-         ****************************************************************/
-        if (!large && !lines) {
-            g.setColor(neutralColor);
-            for (WayPoint trkPnt : visibleSegments) {
-                LatLon c = trkPnt.getCoor();
-                if (Double.isNaN(c.lat()) || Double.isNaN(c.lon())) {
-                    continue;
-                }
-                Point screen = mv.getPoint(trkPnt.getEastNorth());
-                g.setColor(trkPnt.customColoring);
-                g.drawRect(screen.x, screen.y, 0, 0);
-            } // end for trkpnt
-        } // end if large
-
-        if(lineWidth != 0)
-        {
-            g.setStroke(storedStroke);
-        }
-    } // end paint
+        return visibleSegments;
+    }
 
     @Override
@@ -830,5 +345,5 @@
      * additional entries are initialized to true;
      */
-    private final void ensureTrackVisibilityLength() {
+    private void ensureTrackVisibilityLength() {
         final int l = data.tracks.size();
         if (l == trackVisibility.length)
@@ -844,28 +359,5 @@
     public void projectionChanged(Projection oldValue, Projection newValue) {
         if (newValue == null) return;
-        if (data.waypoints != null) {
-            for (WayPoint wp : data.waypoints){
-                wp.invalidateEastNorthCache();
-            }
-        }
-        if (data.tracks != null){
-            for (GpxTrack track: data.tracks) {
-                for (GpxTrackSegment segment: track.getSegments()) {
-                    for (WayPoint wp: segment.getWayPoints()) {
-                        wp.invalidateEastNorthCache();
-                    }
-                }
-            }
-        }
-        if (data.routes != null) {
-            for (GpxRoute route: data.routes) {
-                if (route.routePoints == null) {
-                    continue;
-                }
-                for (WayPoint wp: route.routePoints) {
-                    wp.invalidateEastNorthCache();
-                }
-            }
-        }
+        data.resetEastNorthCache();
     }
 
Index: trunk/src/org/openstreetmap/josm/gui/layer/gpx/DateFilterPanel.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/layer/gpx/DateFilterPanel.java	(revision 7318)
+++ trunk/src/org/openstreetmap/josm/gui/layer/gpx/DateFilterPanel.java	(revision 7319)
@@ -47,5 +47,5 @@
         
         final Date startTime, endTime;
-        Date[] bounds = layer.getMinMaxTimeForAllTracks();
+        Date[] bounds = layer.data.getMinMaxTimeForAllTracks();
         startTime = (bounds==null) ? new GregorianCalendar(2000, 1, 1).getTime():bounds[0];
         endTime = (bounds==null) ? new Date() : bounds[1];
Index: trunk/src/org/openstreetmap/josm/gui/layer/gpx/DownloadAlongTrackAction.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/layer/gpx/DownloadAlongTrackAction.java	(revision 7318)
+++ trunk/src/org/openstreetmap/josm/gui/layer/gpx/DownloadAlongTrackAction.java	(revision 7319)
@@ -44,5 +44,5 @@
      */
     public DownloadAlongTrackAction(GpxData data) {
-        super(tr("Download from OSM along this track"), "downloadalongtrack", null, null, true);
+        super(tr("Download from OSM along this track"), "downloadalongtrack", null, null, false);
         this.data = data;
     }
Index: trunk/src/org/openstreetmap/josm/gui/layer/gpx/GpxDrawHelper.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/layer/gpx/GpxDrawHelper.java	(revision 7319)
+++ trunk/src/org/openstreetmap/josm/gui/layer/gpx/GpxDrawHelper.java	(revision 7319)
@@ -0,0 +1,533 @@
+// License: GPL. See LICENSE file for details.
+
+package org.openstreetmap.josm.gui.layer.gpx;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+import static org.openstreetmap.josm.tools.I18n.marktr;
+
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.Point;
+import java.awt.RenderingHints;
+import java.awt.Stroke;
+import java.util.Collection;
+import java.util.Date;
+import java.util.List;
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.coor.LatLon;
+import org.openstreetmap.josm.data.gpx.GpxData;
+import org.openstreetmap.josm.data.gpx.WayPoint;
+import org.openstreetmap.josm.gui.MapView;
+import org.openstreetmap.josm.tools.ColorScale;
+
+
+/**
+ * Class that helps to draw large set of GPS tracks with different colors and options 
+ * @since 7319
+ */
+public class GpxDrawHelper {
+    private GpxData data;
+    
+    // draw lines between points belonging to different segments
+    private boolean forceLines;
+    // draw direction arrows on the lines
+    private boolean direction;
+    /** don't draw lines if longer than x meters **/
+    private int lineWidth;
+    private int maxLineLength;
+    private boolean lines;
+    /** paint large dots for points **/
+    private boolean large;
+    private int largesize;
+    private boolean hdopCircle;
+    /** paint direction arrow with alternate math. may be faster **/
+    private boolean alternateDirection;
+    /** don't draw arrows nearer to each other than this **/
+    private int delta;
+    private double minTrackDurationForTimeColoring;
+
+    private int hdopfactor;
+
+    private static final double PHI = Math.toRadians(15);
+    
+    //// Variables used only to check cache validity
+    private boolean computeCacheInSync = false;
+    private int computeCacheMaxLineLengthUsed;
+    private Color computeCacheColorUsed;
+    private boolean computeCacheColorDynamic;
+    private ColorMode computeCacheColored;
+    private int computeCacheColorTracksTune;
+
+    //// Color-related fields
+    /** Mode of the line coloring **/
+    private ColorMode colored; 
+    /** max speed for coloring - allows to tweak line coloring for different speed levels. **/
+    private int colorTracksTune;
+    private boolean colorModeDynamic;
+    private Color neutralColor;
+    private int largePointAlpha;
+
+    // default access is used to allow changing from plugins
+    ColorScale velocityScale;
+    /** Colors (without custom alpha channel, if given) for HDOP painting. **/
+    ColorScale hdopScale;
+    ColorScale dateScale;
+    ColorScale directionScale;
+    
+    /** Opacity for hdop points **/
+    private int hdopAlpha;
+    
+
+    // lookup array to draw arrows without doing any math
+    private static final int ll0 = 9;
+    private static final int sl4 = 5;
+    private static final int sl9 = 3;
+    private static final int[][] dir = { { +sl4, +ll0, +ll0, +sl4 }, { -sl9, +ll0, +sl9, +ll0 }, { -ll0, +sl4, -sl4, +ll0 },
+        { -ll0, -sl9, -ll0, +sl9 }, { -sl4, -ll0, -ll0, -sl4 }, { +sl9, -ll0, -sl9, -ll0 },
+        { +ll0, -sl4, +sl4, -ll0 }, { +ll0, +sl9, +ll0, -sl9 }, { +sl4, +ll0, +ll0, +sl4 },
+        { -sl9, +ll0, +sl9, +ll0 }, { -ll0, +sl4, -sl4, +ll0 }, { -ll0, -sl9, -ll0, +sl9 } };
+    
+    private void setupColors() {
+        hdopAlpha = Main.pref.getInteger("hdop.color.alpha", -1);
+        velocityScale = ColorScale.createHSBScale(256).addTitle(tr("Velocity, km/h"));
+        /** Colors (without custom alpha channel, if given) for HDOP painting. **/
+        hdopScale = ColorScale.createHSBScale(256).makeReversed().addTitle(tr("HDOP, m"));
+        dateScale = ColorScale.createHSBScale(256).addTitle(tr("Time"));
+        directionScale = ColorScale.createCyclicScale(256).setIntervalCount(4).addTitle(tr("Direction"));
+    }
+
+    /**
+     * Different color modes
+     */
+    public enum ColorMode {
+        NONE, VELOCITY, HDOP, DIRECTION, TIME
+    }
+    
+    public GpxDrawHelper(GpxData gpxData) {
+        data = gpxData;
+        setupColors();
+    }
+   
+    /**
+     * Get the default color for gps tracks for specified layer 
+     * @param layerName name of the GpxLayer
+     * @param ignoreCustom do not use preferences
+     * @return the color or null if the color is not constant 
+     */
+    public Color getColor(String layerName, boolean ignoreCustom) {
+        Color c = Main.pref.getColor(marktr("gps point"), "layer " + layerName, Color.gray);
+        return ignoreCustom || getColorMode(layerName) == ColorMode.NONE ? c : null;
+    }
+
+    /**
+     * Read coloring mode for specified layer from preferences
+     * @param layerName name of the GpxLayer
+     * @return coloting mode
+     */
+    public ColorMode getColorMode(String layerName) {
+        try {
+            int i=Main.pref.getInteger("draw.rawgps.colors", "layer " + layerName, 0);
+            return ColorMode.values()[i];
+        } catch (Exception e) {
+            Main.warn(e);
+        }
+        return ColorMode.NONE;
+    }
+
+    /** Reads generic color from preferences (usually gray)
+     * @return the color
+     **/
+    public static Color getGenericColor() {
+        return Main.pref.getColor(marktr("gps point"), Color.gray);
+    }
+    
+    /**
+     * Read all drawing-related settings from preferences
+     * @param layerName layer name used to access its specific preferences
+     **/
+    public void readPreferences(String layerName) {
+        String spec = "layer " + layerName;
+        forceLines = Main.pref.getBoolean("draw.rawgps.lines.force", spec, false);
+        direction = Main.pref.getBoolean("draw.rawgps.direction", spec, false);
+        lineWidth = Main.pref.getInteger("draw.rawgps.linewidth", spec, 0);
+
+        if (!data.fromServer) {
+            maxLineLength = Main.pref.getInteger("draw.rawgps.max-line-length.local", spec, -1);
+            lines = Main.pref.getBoolean("draw.rawgps.lines.local", spec, true);
+        } else {
+            maxLineLength = Main.pref.getInteger("draw.rawgps.max-line-length", spec, 200);
+            lines = Main.pref.getBoolean("draw.rawgps.lines", spec, true);
+        }
+        large = Main.pref.getBoolean("draw.rawgps.large", spec, false);
+        largesize = Main.pref.getInteger("draw.rawgps.large.size", spec, 3);
+        hdopCircle = Main.pref.getBoolean("draw.rawgps.hdopcircle", spec, false);
+        colored = getColorMode(layerName);
+        alternateDirection = Main.pref.getBoolean("draw.rawgps.alternatedirection", spec, false);
+        delta = Main.pref.getInteger("draw.rawgps.min-arrow-distance", spec, 40);
+        colorTracksTune = Main.pref.getInteger("draw.rawgps.colorTracksTune", spec, 45);
+        colorModeDynamic = Main.pref.getBoolean("draw.rawgps.colors.dynamic", spec, false);
+        hdopfactor = Main.pref.getInteger("hdop.factor", 25);
+        minTrackDurationForTimeColoring = Main.pref.getInteger("draw.rawgps.date-coloring-min-dt", 60);
+        largePointAlpha = Main.pref.getInteger("draw.rawgps.large.alpha", -1) & 0xFF;
+
+        neutralColor = getColor(layerName, true);
+        velocityScale.setNoDataColor(neutralColor);
+        dateScale.setNoDataColor(neutralColor);
+        hdopScale.setNoDataColor(neutralColor);
+        directionScale.setNoDataColor(neutralColor);
+        
+        largesize += lineWidth;
+    }
+    
+    
+    public void drawAll(Graphics2D g, MapView mv, List<WayPoint> visibleSegments) {
+
+        checkCache();
+        
+        // STEP 2b - RE-COMPUTE CACHE DATA *********************
+        if (!computeCacheInSync) { // don't compute if the cache is good
+            calculateColors();
+        }
+
+        Stroke storedStroke = g.getStroke();
+        
+        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
+            Main.pref.getBoolean("mappaint.gpx.use-antialiasing", false) ?
+                    RenderingHints.VALUE_ANTIALIAS_ON : RenderingHints.VALUE_ANTIALIAS_OFF);
+        
+        if(lineWidth != 0) {
+           g.setStroke(new BasicStroke(lineWidth,BasicStroke.CAP_ROUND,BasicStroke.JOIN_ROUND));
+        }
+        drawLines(g, mv, visibleSegments);
+        drawArrows(g, mv, visibleSegments);
+        drawPoints(g, mv, visibleSegments);
+        if(lineWidth != 0) {
+            g.setStroke(storedStroke);
+        }
+    }
+    
+    public void calculateColors() {
+        double minval = +1e10;
+        double maxval = -1e10;
+        WayPoint oldWp = null;
+        
+        if (colorModeDynamic) {
+            if (colored == ColorMode.VELOCITY) {
+                for (Collection<WayPoint> segment : data.getLinesIterable(null)) {
+                    if(!forceLines) {
+                        oldWp = null;
+                    }
+                    for (WayPoint trkPnt : segment) {
+                        LatLon c = trkPnt.getCoor();
+                        if (Double.isNaN(c.lat()) || Double.isNaN(c.lon())) {
+                            continue;
+                        }
+                        if (oldWp != null && trkPnt.time > oldWp.time) {
+                            double vel = c.greatCircleDistance(oldWp.getCoor())
+                                    / (trkPnt.time - oldWp.time);
+                            if(vel > maxval) {
+                                maxval = vel;
+                            }
+                            if(vel < minval) {
+                                minval = vel;
+                            }
+                        }
+                        oldWp = trkPnt;
+                    }
+                }
+                if (minval >= maxval) {
+                    velocityScale.setRange(0, 120/3.6);
+                } else {
+                    velocityScale.setRange(minval, maxval);
+                }
+            } else if (colored == ColorMode.HDOP) {
+                for (Collection<WayPoint> segment : data.getLinesIterable(null)) {
+                    for (WayPoint trkPnt : segment) {
+                        Object val = trkPnt.attr.get("hdop");
+                        if (val != null) {
+                            double hdop = ((Float) val).doubleValue();
+                            if(hdop > maxval) {
+                                maxval = hdop;
+                            }
+                            if(hdop < minval) {
+                                minval = hdop;
+                            }
+                        }
+                    }
+                }
+                if (minval >= maxval) {
+                    hdopScale.setRange(0, 100);
+                } else {
+                    hdopScale.setRange(minval, maxval);
+                }
+            }
+            oldWp = null;
+        } else { // color mode not dynamic
+            velocityScale.setRange(0, colorTracksTune);
+            hdopScale.setRange(0, 1.0/hdopfactor);
+        }
+        double now = System.currentTimeMillis()/1000.0;
+        if (colored == ColorMode.TIME) {
+            Date[] bounds = data.getMinMaxTimeForAllTracks();
+            if (bounds!=null) {
+                minval = bounds[0].getTime()/1000.0;
+                maxval = bounds[1].getTime()/1000.0;
+            } else {
+                minval = 0; maxval=now;
+            }
+            dateScale.setRange(minval, maxval);
+        }
+
+        
+        // Now the colors for all the points will be assigned
+        for (Collection<WayPoint> segment : data.getLinesIterable(null)) {
+            if (!forceLines) { // don't draw lines between segments, unless forced to
+                oldWp = null;
+            }
+            for (WayPoint trkPnt : segment) {
+                LatLon c = trkPnt.getCoor();
+                trkPnt.customColoring = neutralColor;
+                if (Double.isNaN(c.lat()) || Double.isNaN(c.lon())) {
+                    continue;
+                }
+                 // now we are sure some color will be assigned
+                Color color = null; 
+            
+                if (colored == ColorMode.HDOP) {
+                    Float hdop = ((Float) trkPnt.attr.get("hdop"));
+                    color = hdopScale.getColor(hdop);
+                }
+                if (oldWp != null) { // other coloring modes need segment for calcuation
+                    double dist = c.greatCircleDistance(oldWp.getCoor());
+                    boolean noDraw=false;
+                    switch (colored) {
+                    case VELOCITY:
+                        double dtime = trkPnt.time - oldWp.time;
+                        if(dtime > 0) {
+                            color = velocityScale.getColor(dist / dtime);
+                        } else {
+                            color = velocityScale.getNoDataColor();
+                        }
+                        break;
+                    case DIRECTION:
+                        double dirColor = oldWp.getCoor().heading(trkPnt.getCoor());
+                        color = directionScale.getColor(dirColor);
+                        break;
+                    case TIME:
+                        double t=trkPnt.time;
+                        if (t > 0 && t <= now && maxval - minval > minTrackDurationForTimeColoring) { // skip bad timestamps and very short tracks
+                            color = dateScale.getColor(t);
+                        } else {
+                            color = dateScale.getNoDataColor();
+                        }
+                        break;
+                    }
+                    if (!noDraw && (maxLineLength == -1 || dist <= maxLineLength)) {
+                        trkPnt.drawLine = true;
+                        trkPnt.dir = (int) oldWp.getCoor().heading(trkPnt.getCoor());
+                    } else {
+                        trkPnt.drawLine = false;
+                    } 
+                } else { // make sure we reset outdated data
+                    trkPnt.drawLine = false;
+                    color = neutralColor;
+                }
+               
+                trkPnt.customColoring = color;
+                oldWp = trkPnt;
+            }
+        }
+        
+        computeCacheInSync = true;
+    }
+
+
+  
+    private void drawLines(Graphics2D g, MapView mv, List<WayPoint> visibleSegments) {
+        if (lines) {
+            Point old = null;
+            for (WayPoint trkPnt : visibleSegments) {
+                LatLon c = trkPnt.getCoor();
+                if (Double.isNaN(c.lat()) || Double.isNaN(c.lon())) {
+                    continue;
+                }
+                Point screen = mv.getPoint(trkPnt.getEastNorth());
+                if (trkPnt.drawLine) {
+                    // skip points that are on the same screenposition
+                    if (old != null && ((old.x != screen.x) || (old.y != screen.y))) {
+                        g.setColor(trkPnt.customColoring);
+                        g.drawLine(old.x, old.y, screen.x, screen.y);
+                    }
+                }
+                old = screen;
+            }
+        }
+    }
+
+    private void drawArrows(Graphics2D g, MapView mv, List<WayPoint> visibleSegments) {
+        /****************************************************************
+         ********** STEP 3b - DRAW NICE ARROWS **************************
+         ****************************************************************/
+        if (lines && direction && !alternateDirection) {
+            Point old = null;
+            Point oldA = null; // last arrow painted
+            for (WayPoint trkPnt : visibleSegments) {
+                LatLon c = trkPnt.getCoor();
+                if (Double.isNaN(c.lat()) || Double.isNaN(c.lon())) {
+                    continue;
+                }
+                if (trkPnt.drawLine) {
+                    Point screen = mv.getPoint(trkPnt.getEastNorth());
+                    // skip points that are on the same screenposition
+                    if (old != null
+                            && (oldA == null || screen.x < oldA.x - delta || screen.x > oldA.x + delta
+                            || screen.y < oldA.y - delta || screen.y > oldA.y + delta)) {
+                        g.setColor(trkPnt.customColoring);
+                        double t = Math.atan2(screen.y - old.y, screen.x - old.x) + Math.PI;
+                        g.drawLine(screen.x, screen.y, (int) (screen.x + 10 * Math.cos(t - PHI)),
+                                (int) (screen.y + 10 * Math.sin(t - PHI)));
+                        g.drawLine(screen.x, screen.y, (int) (screen.x + 10 * Math.cos(t + PHI)),
+                                (int) (screen.y + 10 * Math.sin(t + PHI)));
+                        oldA = screen;
+                    }
+                    old = screen;
+                }
+            } // end for trkpnt
+        } 
+        
+        /****************************************************************
+         ********** STEP 3c - DRAW FAST ARROWS **************************
+         ****************************************************************/
+        if (lines && direction && alternateDirection) {
+            Point old = null;
+            Point oldA = null; // last arrow painted
+            for (WayPoint trkPnt : visibleSegments) {
+                LatLon c = trkPnt.getCoor();
+                if (Double.isNaN(c.lat()) || Double.isNaN(c.lon())) {
+                    continue;
+                }
+                if (trkPnt.drawLine) {
+                    Point screen = mv.getPoint(trkPnt.getEastNorth());
+                    // skip points that are on the same screenposition
+                    if (old != null
+                            && (oldA == null || screen.x < oldA.x - delta || screen.x > oldA.x + delta
+                            || screen.y < oldA.y - delta || screen.y > oldA.y + delta)) {
+                        g.setColor(trkPnt.customColoring);
+                        g.drawLine(screen.x, screen.y, screen.x + dir[trkPnt.dir][0], screen.y
+                                + dir[trkPnt.dir][1]);
+                        g.drawLine(screen.x, screen.y, screen.x + dir[trkPnt.dir][2], screen.y
+                                + dir[trkPnt.dir][3]);
+                        oldA = screen;
+                    }
+                    old = screen;
+                }
+            } // end for trkpnt
+        } 
+    }
+
+    private void drawPoints(Graphics2D g, MapView mv, List<WayPoint> visibleSegments) {
+        /****************************************************************
+         ********** STEP 3d - DRAW LARGE POINTS AND HDOP CIRCLE *********
+         ****************************************************************/
+        if (large || hdopCircle) {
+            final int halfSize = largesize/2;
+            for (WayPoint trkPnt : visibleSegments) {
+                LatLon c = trkPnt.getCoor();
+                if (Double.isNaN(c.lat()) || Double.isNaN(c.lon())) {
+                    continue;
+                }
+                Point screen = mv.getPoint(trkPnt.getEastNorth());
+                
+                    
+                if (hdopCircle && trkPnt.attr.get("hdop") != null) {
+                    // hdop value
+                    float hdop = ((Float)trkPnt.attr.get("hdop"));
+                    if (hdop < 0) {
+                        hdop = 0;
+                    }
+                    Color customColoringTransparent = hdopAlpha<0 ? trkPnt.customColoring:
+                        new Color(trkPnt.customColoring.getRGB() & 0x00ffffff | hdopAlpha<<24, true);
+                    g.setColor(customColoringTransparent);
+                    // hdop cirles
+                    int hdopp = mv.getPoint(new LatLon(trkPnt.getCoor().lat(), trkPnt.getCoor().lon() + 2*6*hdop*360/40000000)).x - screen.x;
+                    g.drawArc(screen.x-hdopp/2, screen.y-hdopp/2, hdopp, hdopp, 0, 360);
+                }
+                if (large) {
+                    // color the large GPS points like the gps lines
+                    Color customColoringTransparent = largePointAlpha<0 ? trkPnt.customColoring: 
+                        new Color(trkPnt.customColoring.getRGB() & 0x00ffffff | largePointAlpha<<24, true);
+                    
+                    g.setColor(customColoringTransparent);
+                    g.fillRect(screen.x-halfSize, screen.y-halfSize, largesize, largesize);
+                }
+            } // end for trkpnt
+        } // end if large || hdopcircle
+
+        /****************************************************************
+         ********** STEP 3e - DRAW SMALL POINTS FOR LINES ***************
+         ****************************************************************/
+        if (!large && lines) {
+            g.setColor(neutralColor);
+            for (WayPoint trkPnt : visibleSegments) {
+                LatLon c = trkPnt.getCoor();
+                if (Double.isNaN(c.lat()) || Double.isNaN(c.lon())) {
+                    continue;
+                }
+                if (!trkPnt.drawLine) {
+                    Point screen = mv.getPoint(trkPnt.getEastNorth());
+                    g.drawRect(screen.x, screen.y, 0, 0);
+                }
+            } // end for trkpnt
+        } // end if large
+
+        /****************************************************************
+         ********** STEP 3f - DRAW SMALL POINTS INSTEAD OF LINES ********
+         ****************************************************************/
+        if (!large && !lines) {
+            g.setColor(neutralColor);
+            for (WayPoint trkPnt : visibleSegments) {
+                LatLon c = trkPnt.getCoor();
+                if (Double.isNaN(c.lat()) || Double.isNaN(c.lon())) {
+                    continue;
+                }
+                Point screen = mv.getPoint(trkPnt.getEastNorth());
+                g.setColor(trkPnt.customColoring);
+                g.drawRect(screen.x, screen.y, 0, 0);
+            } // end for trkpnt
+        } // end if large
+    }
+
+    /**
+     * Check cache validity set necessary flags
+     */
+    private void checkCache() {
+        if ((computeCacheMaxLineLengthUsed != maxLineLength) || (!neutralColor.equals(computeCacheColorUsed))
+                || (computeCacheColored != colored) || (computeCacheColorTracksTune != colorTracksTune)
+                || (computeCacheColorDynamic != colorModeDynamic)) {
+            computeCacheMaxLineLengthUsed = maxLineLength;
+            computeCacheInSync = false;
+            computeCacheColorUsed = neutralColor;
+            computeCacheColored = colored;
+            computeCacheColorTracksTune = colorTracksTune;
+            computeCacheColorDynamic = colorModeDynamic;
+        }
+    }
+
+    public void dataChanged() {
+        computeCacheInSync = false;
+    }
+    
+    public void drawColorBar(Graphics2D g, MapView mv) {
+        int w = mv.getWidth();
+        int h = mv.getHeight();
+        if (colored == ColorMode.HDOP) {
+            hdopScale.drawColorBar(g, w-30, 50, 20, 100, 1.0);
+        } else if (colored == ColorMode.VELOCITY) {
+            velocityScale.drawColorBar(g, w-30, 50, 20, 100, 3.6);
+        } else if (colored == ColorMode.DIRECTION) {
+            directionScale.drawColorBar(g, w-30, 50, 20, 100, 180.0/Math.PI);
+        }
+    }
+    
+}
Index: trunk/src/org/openstreetmap/josm/gui/preferences/display/ColorPreference.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/preferences/display/ColorPreference.java	(revision 7318)
+++ trunk/src/org/openstreetmap/josm/gui/preferences/display/ColorPreference.java	(revision 7319)
@@ -38,4 +38,5 @@
 import org.openstreetmap.josm.gui.conflict.ConflictColors;
 import org.openstreetmap.josm.gui.dialogs.ConflictDialog;
+import org.openstreetmap.josm.gui.layer.gpx.GpxDrawHelper;
 import org.openstreetmap.josm.gui.layer.GpxLayer;
 import org.openstreetmap.josm.gui.layer.ImageryLayer;
@@ -266,5 +267,5 @@
         Severity.getColors();
         MarkerLayer.getGenericColor();
-        GpxLayer.getGenericColor();
+        GpxDrawHelper.getGenericColor();
         OsmDataLayer.getOutsideColor();
         ImageryLayer.getFadeColor();
Index: trunk/src/org/openstreetmap/josm/tools/ColorScale.java
===================================================================
--- trunk/src/org/openstreetmap/josm/tools/ColorScale.java	(revision 7319)
+++ trunk/src/org/openstreetmap/josm/tools/ColorScale.java	(revision 7319)
@@ -0,0 +1,182 @@
+// License: GPL. For details, see LICENSE file.
+
+package org.openstreetmap.josm.tools;
+
+import java.awt.Color;
+import java.awt.FontMetrics;
+import java.awt.Graphics2D;
+
+/**
+ * Utility class that helps to work with color scale 
+ * for coloring GPX tracks etc.
+ * @since 7319
+ */
+public class ColorScale {
+    private double min, max;
+    private Color noDataColor;
+    private Color belowMinColor;
+    private Color aboveMaxColor;
+    
+    private Color[] colors;
+    private String title = "";
+    private int intervalCount = 5;
+    
+    private ColorScale() {
+        
+    }
+    
+    public static ColorScale createHSBScale(int count) {
+        ColorScale sc = new ColorScale();
+        sc.colors = new Color[count];
+        for (int i = 0; i < count; i++) {
+            sc.colors[i] = Color.getHSBColor(i / 300.0f, 1, 1);
+        }
+        sc.setRange(0, 255);
+        sc.addBounds();
+        return sc;
+    }
+    
+    public static ColorScale createCyclicScale(int count) {
+        ColorScale sc = new ColorScale();
+        //                    red   yellow  green   blue    red
+        int[] h = new int[] { 0,    59,     127,    244,    360};
+        int[] s = new int[] { 100,  84,     99,     100 };
+        int[] b = new int[] { 90,   93,     74,     83 };
+
+        sc.colors = new Color[count];
+        for (int i = 0; i < sc.colors.length; i++) {
+  
+            float angle = 4 - i / 256f * 4;
+            int quadrant = (int) angle;
+            angle -= quadrant;
+            quadrant = Utils.mod(quadrant+1, 4);
+
+            float vh = h[quadrant] * w(angle) + h[quadrant+1] * (1 - w(angle));
+            float vs = s[quadrant] * w(angle) + s[Utils.mod(quadrant+1, 4)] * (1 - w(angle));
+            float vb = b[quadrant] * w(angle) + b[Utils.mod(quadrant+1, 4)] * (1 - w(angle));
+
+            sc.colors[i] = Color.getHSBColor(vh/360f, vs/100f, vb/100f);
+        }
+        sc.setRange(0, 2*Math.PI);
+        sc.addBounds();
+        return sc;
+    }
+    
+    /**
+     * transition function:
+     *  w(0)=1, w(1)=0, 0&lt;=w(x)&lt;=1
+     * @param x number: 0&lt;=x&lt;=1
+     * @return the weighted value
+     */
+    private static float w(float x) {
+        if (x < 0.5)
+            return 1 - 2*x*x;
+        else
+            return 2*(1-x)*(1-x);
+    }
+
+    public void setRange(double min, double max) {
+        this.min = min;
+        this.max = max;
+    }
+    
+    /**
+     * Add standard colors for values below min or above max value
+     */
+    public void addBounds() {
+        aboveMaxColor = colors[colors.length-1];
+        belowMinColor = colors[0];
+    }
+    
+    public final Color getColor(double value) {
+        if (value<min) return belowMinColor;
+        if (value>max) return aboveMaxColor;
+        if (Double.isNaN(value)) return noDataColor;
+        final int n = colors.length;
+        int idx = (int) ((value-min)*colors.length / (max-min));
+        if (idx<colors.length) {
+            return colors[idx];
+        } else {
+            return colors[n-1]; // this happens when value==max
+        }
+        // int hdoplvl =(int) Math.round(colorModeDynamic ? ((hdop-minval)*255/(maxval-minval))
+        //            : (hdop <= 0 ? 0 : hdop * hdopfactor));
+        // int hdopcolor = 255 - (hdoplvl > 255 ? 255 : hdoplvl);
+    }
+    
+    public final Color getColor(Number value) {
+        return (value==null)? noDataColor : getColor(value.doubleValue());
+    }
+
+    public Color getNoDataColor() {
+        
+        return noDataColor;
+    }
+
+    public void setNoDataColor(Color noDataColor) {
+        this.noDataColor = noDataColor;
+    }
+    
+    public ColorScale makeTransparent(int alpha) {
+        for (int i = 0; i < colors.length; i++) {
+            colors[i] = new Color((colors[i].getRGB() & 0xFFFFFF) | ((alpha & 0xFF) << 24), true);
+        }
+        return this;
+    }
+    
+    public ColorScale addTitle(String title) {
+        this.title = title;
+        return this;
+    }
+    
+    public ColorScale setIntervalCount(int intervalCount) {
+        this.intervalCount = intervalCount;
+        return this;
+    }
+    
+    public ColorScale makeReversed() {
+        int n = colors.length;
+        Color tmp;
+        for (int i=0; i<n/2; i++) {
+            tmp = colors[i];
+            colors[i] = colors[n-1-i];
+            colors[n-1-i] = tmp;
+        }
+        tmp = belowMinColor;
+        belowMinColor = aboveMaxColor;
+        aboveMaxColor = tmp;
+        return this;
+    }
+    
+    public void drawColorBar(Graphics2D g, int x, int y, int w, int h, double valueScale) {
+        int n=colors.length;
+        
+        for (int i=0; i<n; i++) {
+            g.setColor(colors[i]);
+            if (w<h) {
+                g.fillRect(x, y+i*h/n, w, h/n+1);
+            } else {
+                g.fillRect(x+i*w/n, y, w/n+1, h);
+            }
+        }
+        
+        int fw, fh;
+        FontMetrics fm = g.getFontMetrics(); 
+        fh = fm.getHeight()/2;
+        fw = fm.stringWidth(String.valueOf(Math.max((int)Math.abs(max*valueScale), (int)Math.abs(min*valueScale)))) + fm.stringWidth("0.123");
+        g.setColor(noDataColor);
+        if (title != null) {
+            g.drawString(title, x-fw-3, y-fh*3/2);
+        }
+        for (int i=0; i<=intervalCount; i++) {
+            g.setColor(colors[(int)(1.0*i*n/intervalCount-1e-10)]);
+            final double val =  min+i*(max-min)/intervalCount;
+            final String txt = String.format("%.3f", val*valueScale);
+            if (w<h) {
+                g.drawString(txt, x-fw-3, y+i*h/intervalCount+fh/2);
+            } else {
+                g.drawString(txt, x+i*w/intervalCount-fw/2, y+fh-3);
+            }
+        }
+    }
+}
