Index: trunk/src/org/openstreetmap/josm/gui/layer/gpx/GpxDrawHelper.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/layer/gpx/GpxDrawHelper.java	(revision 19235)
+++ trunk/src/org/openstreetmap/josm/gui/layer/gpx/GpxDrawHelper.java	(revision 19236)
@@ -237,4 +237,10 @@
     private boolean gpxLayerInvalidated;
 
+    /** minTime saves the start time of the track as epoch seconds */
+    private double minTime;
+    /** maxTime saves the end time of the track as epoch seconds */
+    private double maxTime;
+
+
     private void setupColors() {
         hdopAlpha = Config.getPref().getInt("hdop.color.alpha", -1);
@@ -243,8 +249,8 @@
         hdopScale = ColorScale.createHSBScale(256).makeReversed().addTitle(tr("HDOP"));
         qualityScale = ColorScale.createFixedScale(rtkLibQualityColors).addTitle(tr("Quality")).addColorBarTitles(rtkLibQualityNames);
-        fixScale = ColorScale.createFixedScale(gpsFixQualityColors).addTitle(tr("GPS fix")).addColorBarTitles(gpsFixQualityNames);
-        refScale = ColorScale.createCyclicScale(1).addTitle(tr("GPS ref"));
-        dateScale = ColorScale.createHSBScale(256).addTitle(tr("Time"));
-        directionScale = ColorScale.createCyclicScale(256).setIntervalCount(4).addTitle(tr("Direction"));
+        fixScale = ColorScale.createFixedScale(gpsFixQualityColors).addTitle(tr("GPS fix value")).addColorBarTitles(gpsFixQualityNames);
+        refScale = ColorScale.createCyclicScale(1).addTitle(tr("GPS Ref-ID"));
+        dateScale = ColorScale.createHSBScale(256).addTitle(tr("Track date"));
+        directionScale = ColorScale.createCyclicScale(256).setIntervalCount(4).addTitle(tr("Direction [°]"));
 
         systemOfMeasurementChanged(null, null);
@@ -254,5 +260,5 @@
     public void systemOfMeasurementChanged(String oldSoM, String newSoM) {
         SystemOfMeasurement som = SystemOfMeasurement.getSystemOfMeasurement();
-        velocityScale.addTitle(tr("Velocity, {0}", som.speedName));
+        velocityScale.addTitle(tr("Velocity [{0}]", som.speedName));
         layer.invalidate();
     }
@@ -623,4 +629,7 @@
             minval = interval.getStart().getEpochSecond();
             maxval = interval.getEnd().getEpochSecond();
+            this.minTime = minval;
+            this.maxTime = maxval;
+
             dateScale.setRange(minval, maxval);
         }
@@ -642,5 +651,5 @@
                 Collections.sort(refs);
                 String[] a = {};
-                refScale = ColorScale.createCyclicScale(refs.size()).addTitle(tr("GPS ref")).addColorBarTitles(refs.toArray(a));
+                refScale = ColorScale.createCyclicScale(refs.size()).addTitle(tr("GPS ref ID")).addColorBarTitles(refs.toArray(a));
                 refScale.setRange(0, refs.size());
             }
@@ -1619,16 +1628,18 @@
 
         if (colored == ColorMode.HDOP) {
-            hdopScale.drawColorBar(g, w-30, 50, 20, 100, 1.0);
+            hdopScale.drawColorBar(g, w-10, 50, 20, 100, 1.0);
         } else if (colored == ColorMode.QUALITY) {
-            qualityScale.drawColorBar(g, w-30, 50, 20, 100, 1.0);
+            qualityScale.drawColorBar(g, w-10, 50, 20, 100, 1.0);
         } else if (colored == ColorMode.FIX) {
-            fixScale.drawColorBar(g, w-30, 50, 20, 175, 1.0);
+            fixScale.drawColorBar(g, w-10, 50, 20, 175, 1.0);
         } else if (colored == ColorMode.REF) {
-            refScale.drawColorBar(g, w-30, 50, 20, 175, 1.0);
+            refScale.drawColorBar(g, w-10, 50, 20, 175, 1.0);
         } else if (colored == ColorMode.VELOCITY) {
             SystemOfMeasurement som = SystemOfMeasurement.getSystemOfMeasurement();
-            velocityScale.drawColorBar(g, w-30, 50, 20, 100, som.speedValue);
+            velocityScale.drawColorBar(g, w-10, 50, 20, 100, som.speedValue);
         } else if (colored == ColorMode.DIRECTION) {
-            directionScale.drawColorBar(g, w-30, 50, 20, 100, 180.0/Math.PI);
+            directionScale.drawColorBar(g, w-10, 50, 20, 100, 180.0/Math.PI);
+        } else if (colored == ColorMode.TIME) {
+            dateScale.drawColorBarTime(g, w-10, 50, 20, 100, this.minTime, this.maxTime);
         }
     }
Index: trunk/src/org/openstreetmap/josm/tools/ColorHelper.java
===================================================================
--- trunk/src/org/openstreetmap/josm/tools/ColorHelper.java	(revision 19235)
+++ trunk/src/org/openstreetmap/josm/tools/ColorHelper.java	(revision 19236)
@@ -74,7 +74,11 @@
     public static Color getForegroundColor(Color bg) {
         // http://stackoverflow.com/a/3943023/2257172
-        return bg == null ? null :
-              (bg.getRed()*0.299 + bg.getGreen()*0.587 + bg.getBlue()*0.114) > 186 ?
-                  Color.BLACK : Color.WHITE;
+        if (bg == null) {
+            return null;
+        }
+        if (calculateContrastRatio(Color.WHITE, bg) > calculateContrastRatio(Color.BLACK, bg)) {
+            return Color.WHITE;
+        }
+        return Color.BLACK;
     }
 
@@ -129,3 +133,43 @@
         return new Color(255 - clr.getRed(), 255 - clr.getGreen(), 255 - clr.getBlue(), clr.getAlpha());
     }
+
+    /**
+     * Calculate the relative "luminance" of a color. This is mostly useful for choosing background/foreground colours
+     * @see <a href="https://stackoverflow.com/questions/9733288/how-to-programmatically-calculate-the-contrast-ratio-between-two-colors">
+     *     constrast ratio</a>
+     */
+    private static double calculateLuminance(Color color) {
+        final double rs = color.getRed() / 255.0;
+        final double gs = color.getGreen() / 255.0;
+        final double bs = color.getBlue() / 255.0;
+        final double r = calculateLuminanceStepFunction(rs);
+        final double g = calculateLuminanceStepFunction(gs);
+        final double b = calculateLuminanceStepFunction(bs);
+        return 0.2126 * r + 0.7152 * g + 0.0722 * b;
+    }
+
+    /**
+     * This is a step function for {@link #calculateLuminance(Color)}
+     * @param color The color to get the values for
+     * @return The value to use when calculating relative luminance
+     */
+    private static double calculateLuminanceStepFunction(double color) {
+        if (color <= 0.03928) {
+            return color / 12.92;
+        }
+        return Math.pow((color + 0.055) / 1.055, 2.4);
+    }
+
+    /**
+     * Calculate the contrast between two colors (e.g. {@link Color#black} and {@link Color#white}).
+     * @param first The first color to use
+     * @param second The second color to use
+     * @return The contrast ratio ((L1 + 0.05)/(L2 + 0.05))
+     * @since 19236
+     */
+    public static double calculateContrastRatio(Color first, Color second) {
+        final double fL = calculateLuminance(first);
+        final double sL = calculateLuminance(second);
+        return (Math.max(fL, sL) + 0.05) / (Math.min(fL, sL) + 0.05);
+    }
 }
Index: trunk/src/org/openstreetmap/josm/tools/ColorScale.java
===================================================================
--- trunk/src/org/openstreetmap/josm/tools/ColorScale.java	(revision 19235)
+++ trunk/src/org/openstreetmap/josm/tools/ColorScale.java	(revision 19236)
@@ -1,9 +1,21 @@
 // License: GPL. For details, see LICENSE file.
 package org.openstreetmap.josm.tools;
+
+import static org.openstreetmap.josm.tools.I18n.marktr;
 
 import java.awt.Color;
 import java.awt.FontMetrics;
 import java.awt.Graphics2D;
+import java.awt.Font;
 import java.util.Arrays;
+import java.util.Date;
+import java.time.ZoneId;
+import java.time.Instant;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+
+import org.openstreetmap.josm.data.preferences.NamedColorProperty;
+
+import javax.swing.UIManager;
 
 /**
@@ -12,4 +24,16 @@
  */
 public final class ColorScale {
+    private static final Color LEGEND_BACKGROUND = new NamedColorProperty(marktr("gpx legend background"), new Color(180, 180, 180, 160)).get();
+    private static final Color LEGEND_TEXT_OUTLINE_DARK = new NamedColorProperty(marktr("gpx legend text outline dark"),
+            new Color(102, 102, 102)).get();
+    private static final Color LEGEND_TEXT_OUTLINE_BRIGHT = new NamedColorProperty(marktr("gpx legend text outline bright"),
+            new Color(204, 204, 204)).get();
+    private static final Color LEGEND_TITLE = new NamedColorProperty(marktr("gpx legend title color"), new Color(0, 0, 0)).get();
+
+    private static final String DAY_TIME_FORMAT = "yyyy-MM-dd      HH:mm";
+    private static final String TIME_FORMAT = "HH:mm:ss";
+    /** Padding for the legend (from the text to the edge of the rectangle) */
+    private static final byte PADDING = 19;
+
     private double min, max;
     private Color noDataColor;
@@ -226,4 +250,27 @@
 
     /**
+     * draws an outline for the legend texts
+     * @param g The graphics to draw on
+     * @param txt The text to draw the outline
+     * @param x Text x
+     * @param y Text y
+     * @param color The color of the text
+     */
+    private void drawOutline(final Graphics2D g, final String txt, final int x, final int y, final Color color) {
+        if (ColorHelper.calculateContrastRatio(color, LEGEND_TEXT_OUTLINE_DARK) >=
+            ColorHelper.calculateContrastRatio(color, LEGEND_TEXT_OUTLINE_BRIGHT)) {
+            g.setColor(LEGEND_TEXT_OUTLINE_DARK);
+        } else {
+            g.setColor(LEGEND_TEXT_OUTLINE_BRIGHT);
+        }
+
+        g.drawString(txt, x -1, y -1);
+        g.drawString(txt, x +1, y -1);
+        g.drawString(txt, x -1, y +1);
+        g.drawString(txt, x +1, y +1);
+        g.setColor(color);
+    }
+
+    /**
      * Draws a color bar representing this scale on the given graphics
      * @param g The graphics to draw on
@@ -234,20 +281,18 @@
      * @param valueScale The scale factor of the values
      */
-    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;
+    public void drawColorBar(final Graphics2D g, final int x, final int y, final int w, final int h, final double valueScale) {
+        final int n = colors.length;
+
+        final FontMetrics fm = calculateFontMetrics(g);
+
+        g.setColor(LEGEND_BACKGROUND);
+
+        // color bar texts width & height
+        final int fw;
+        final int fh = fm.getHeight() / 2;
+
+        // calculates the width of the color bar texts
         if (colorBarTitles != null && colorBarTitles.length > 0) {
-             fw = Arrays.stream(colorBarTitles).mapToInt(fm::stringWidth).max().orElse(50);
+            fw = Arrays.stream(colorBarTitles).mapToInt(fm::stringWidth).max().orElse(50);
         } else {
             fw = fm.stringWidth(
@@ -255,11 +300,161 @@
                     + fm.stringWidth("0.123");
         }
+
+        // background rectangle
+        final int[] t = drawBackgroundRectangle(g, x, y, w, h, fw, fh, fm.stringWidth(title));
+        final int xRect = t[0];
+        final int rectWidth = t[1];
+        final int xText = t[2];
+        final int titleWidth = t[3];
+
+        // colorbar
+        for (int i = 0; i < n; i++) {
+            g.setColor(colors[i]);
+            if (w < h) {
+                double factor = n == 6 ? 1.2 : 1.07 + (0.045 * Math.log(n));
+                if (n < 200) {
+                    g.fillRect(xText + fw + PADDING / 3, y - PADDING / 2 + i * (int) ((double) h / n * factor),
+                            w, (int) ((double) h / n * factor));
+                } else {
+                    g.fillRect(xText + fw + PADDING / 3, y - PADDING / 2 + i * h / (int) (n * 0.875), w, (h / n + 1));
+                }
+            } else {
+                g.fillRect(xText + fw + 7 + i * w / n, y, w / n, h + 1);
+            }
+        }
+
+        // legend title
+        if (title != null) {
+            g.setColor(LEGEND_TITLE);
+            g.drawString(title, xRect + rectWidth / 2 - titleWidth / 2, y - fh * 3 / 2 - 10);
+        }
+
+        // legend texts
+        drawLegend(g, y, w, h, valueScale, fh, fw, xText);
+
         g.setColor(noDataColor);
+    }
+
+    /**
+     * Draws a color bar representing the time scale on the given graphics
+     * @param g The graphics to draw on
+     * @param x Rect x
+     * @param y Rect y
+     * @param w Color bar width
+     * @param h Color bar height
+     * @param minVal start time of the track
+     * @param maxVal end time of the track
+     */
+    public void drawColorBarTime(final Graphics2D g, final int x, final int y, final int w, final int h,
+                                 final double minVal, final double maxVal) {
+        final int n = colors.length;
+
+        final FontMetrics fm = calculateFontMetrics(g);
+
+        g.setColor(LEGEND_BACKGROUND);
+
+        final int padding = PADDING;
+
+        // color bar texts width & height
+        final int fw;
+        final int fh = fm.getHeight() / 2;
+
+        // calculates the width of the colorbar texts
+        if (maxVal - minVal > 86400) {
+            fw = fm.stringWidth(DAY_TIME_FORMAT);
+        } else {
+            fw = fm.stringWidth(TIME_FORMAT);
+        }
+
+        // background rectangle
+        final int[] t = drawBackgroundRectangle(g, x, y, w, h, fw, fh, fm.stringWidth(title));
+        final int xRect = t[0];
+        final int rectWidth = t[1];
+        final int xText = t[2];
+        final int titleWidth = t[3];
+
+        // colorbar
+        for (int i = 0; i < n; i++) {
+            g.setColor(colors[i]);
+            if (w < h) {
+                g.fillRect(xText + fw + padding / 3, y - padding / 2 + i * h / (int) (n * 0.875), w, (h / n + 1));
+            } else {
+                g.fillRect(xText + fw + padding / 3 + i * w / n, y, w / n + 1, h);
+            }
+        }
+
+        // legend title
         if (title != null) {
-            g.drawString(title, x-fw-3, y-fh*3/2);
-        }
+            g.setColor(LEGEND_TITLE);
+            g.drawString(title, xRect + rectWidth / 2 - titleWidth / 2, y - fh * 3 / 2 - padding / 2);
+        }
+
+        // legend texts
+        drawTimeLegend(g, y, x, h, minVal, maxVal, fh, fw, xText);
+
+        g.setColor(noDataColor);
+    }
+
+    private static FontMetrics calculateFontMetrics(final Graphics2D g) {
+        final Font newFont = UIManager.getFont("PopupMenu.font");
+        g.setFont(newFont);
+        return g.getFontMetrics();
+    }
+
+    /**
+     * Draw the background rectangle
+     * @param g The graphics to draw on
+     * @param x Rect x
+     * @param y Rect y
+     * @param w Color bar width
+     * @param h Color bar height
+     * @param fw The font width
+     * @param fh The font height
+     * @param titleWidth The width of the title
+     * @return an @{code int[]} of [xRect, rectWidth, xText, titleWidth] TODO investigate using records in Java 17
+     */
+    private int[] drawBackgroundRectangle(final Graphics2D g, final int x, final int y,
+                                          final int w, final int h, final int fw, final int fh,
+                                          int titleWidth) {
+        final int xRect;
+        final int rectWidth;
+        final int xText;
+        final int arcWidth = 20;
+        final int arcHeight = 20;
+        if (fw + w > titleWidth) {
+            rectWidth = w + fw + PADDING * 2;
+            xRect = x - rectWidth;
+            xText = xRect + (int) (PADDING / 1.2);
+            g.fillRoundRect(xRect, (fh * 3 / 2), rectWidth, h + y - (fh * 3 / 2) + (int) (PADDING / 1.5), arcWidth, arcHeight);
+        } else {
+            if (titleWidth >= 120) {
+                titleWidth = 120;
+            }
+            rectWidth = w + titleWidth + PADDING + PADDING / 2;
+            xRect = x - rectWidth;
+            xText = xRect + PADDING / 2 + rectWidth / 2 - fw;
+            g.fillRoundRect(xRect, (fh * 3 / 2), rectWidth, h + y - (fh * 3 / 2) + (int) (PADDING / 1.5), arcWidth, arcHeight);
+        }
+        return new int[] {xRect, rectWidth, xText, titleWidth};
+    }
+
+    /**
+     * Draws the legend for the color bar representing the time scale on the given graphics
+     * @param g The graphics to draw on
+     * @param y Rect y
+     * @param w Color bar width
+     * @param h Color bar height
+     * @param fw The font width
+     * @param fh The font height
+     * @param valueScale The scale factor of the values
+     * @param xText The location to start drawing the text (x-axis)
+     */
+    private void drawLegend(final Graphics2D g, final int y, final int w, final int h, final double valueScale,
+                            final int fh, final int fw, final int xText) {
         for (int i = 0; i <= intervalCount; i++) {
-            g.setColor(colors[(int) (1.0*i*n/intervalCount-1e-10)]);
-            String txt;
+            final String txt;
+            final Color color = colors[(int) (1.0 * i * colors.length / intervalCount - 1e-10)];
+            g.setColor(color);
+
             if (colorBarTitles != null && i < colorBarTitles.length) {
                 txt = colorBarTitles[i];
@@ -268,11 +463,83 @@
                 txt = String.format("%.3f", val*valueScale);
             }
-            if (intervalCount == 0) {
-                g.drawString(txt, x-fw-3, y+h/2+fh/2);
-            } else if (w < h) {
-                g.drawString(txt, x-fw-3, y+i*h/intervalCount+fh/2);
+            drawLegendText(g, y, w, h, fh, fw, xText, i, color, txt);
+        }
+    }
+
+    /**
+     * Draws the legend for the color bar representing the time scale on the given graphics
+     * @param g The graphics to draw on
+     * @param y Rect y
+     * @param w Color bar width
+     * @param h Color bar height
+     * @param minVal start time of the track
+     * @param maxVal end time of the track
+     * @param fw The font width
+     * @param fh The font height
+     * @param xText The location to start drawing the text (x-axis)
+     */
+    private void drawTimeLegend(final Graphics2D g, final int y, final int w, final int h,
+                                final double minVal, final double maxVal,
+                                final int fh, final int fw, final int xText) {
+        for (int i = 0; i <= intervalCount; i++) {
+            final String txt;
+            final Color color = colors[(int) (1.0 * i * colors.length / intervalCount - 1e-10)];
+            g.setColor(color);
+
+            if (colorBarTitles != null && i < colorBarTitles.length) {
+                txt = colorBarTitles[i];
             } else {
-                g.drawString(txt, x+i*w/intervalCount-fw/2, y+fh-3);
+                final double val = minVal + i * (maxVal - minVal) / intervalCount;
+                final long longval = (long) val;
+
+                final Date date = new Date(longval * 1000L);
+                final Instant dateInst = date.toInstant();
+
+                final ZoneId gmt = ZoneId.of("GMT");
+                final ZonedDateTime zonedDateTime = dateInst.atZone(gmt);
+
+                String formatted;
+
+                if (maxVal-minVal > 86400) {
+                    final DateTimeFormatter day = DateTimeFormatter.ofPattern(DAY_TIME_FORMAT);
+                    formatted = zonedDateTime.format(day);
+                } else {
+                    final DateTimeFormatter time = DateTimeFormatter.ofPattern(TIME_FORMAT);
+                    formatted = zonedDateTime.format(time);
+                }
+
+                txt = formatted;
             }
+            drawLegendText(g, y, w, h, fh, fw, xText, i, color, txt);
+        }
+    }
+
+    /**
+     * Draws the legend for the color bar representing the time scale on the given graphics
+     * @param g The graphics to draw on
+     * @param y Rect y
+     * @param w Color bar width
+     * @param h Color bar height
+     * @param fw The font width
+     * @param fh The font height
+     * @param xText The location to start drawing the text (x-axis)
+     * @param color The color of the text to draw
+     * @param txt The text string to draw
+     * @param i The index of the legend (so we can calculate the y location)
+     */
+    private void drawLegendText(Graphics2D g, int y, int w, int h, int fh, int fw, int xText,
+                                int i, Color color, String txt) {
+
+        if (intervalCount == 0) {
+            drawOutline(g, txt, xText, y + h / 2 + fh / 2, color);
+            g.drawString(txt, xText, y + h / 2 + fh / 2);
+        } else if (w < h) {
+            drawOutline(g, txt, xText, y + i * h / intervalCount + fh / 2, color);
+            g.drawString(txt, xText, y + i * h / intervalCount + fh / 2);
+        } else {
+            final int xLoc = xText + i * w / intervalCount - fw / 2 - (int) (PADDING / 1.3);
+            final int yLoc = y + fh - 5;
+            drawOutline(g, txt, xLoc, yLoc, color);
+            g.drawString(txt, xLoc, yLoc);
         }
     }
