Changeset 19236 in josm
- Timestamp:
- 2024-10-08T19:56:20+02:00 (6 weeks ago)
- Location:
- trunk/src/org/openstreetmap/josm
- Files:
-
- 3 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/org/openstreetmap/josm/gui/layer/gpx/GpxDrawHelper.java
r19106 r19236 237 237 private boolean gpxLayerInvalidated; 238 238 239 /** minTime saves the start time of the track as epoch seconds */ 240 private double minTime; 241 /** maxTime saves the end time of the track as epoch seconds */ 242 private double maxTime; 243 244 239 245 private void setupColors() { 240 246 hdopAlpha = Config.getPref().getInt("hdop.color.alpha", -1); … … 243 249 hdopScale = ColorScale.createHSBScale(256).makeReversed().addTitle(tr("HDOP")); 244 250 qualityScale = ColorScale.createFixedScale(rtkLibQualityColors).addTitle(tr("Quality")).addColorBarTitles(rtkLibQualityNames); 245 fixScale = ColorScale.createFixedScale(gpsFixQualityColors).addTitle(tr("GPS fix ")).addColorBarTitles(gpsFixQualityNames);246 refScale = ColorScale.createCyclicScale(1).addTitle(tr("GPS ref"));247 dateScale = ColorScale.createHSBScale(256).addTitle(tr("T ime"));248 directionScale = ColorScale.createCyclicScale(256).setIntervalCount(4).addTitle(tr("Direction "));251 fixScale = ColorScale.createFixedScale(gpsFixQualityColors).addTitle(tr("GPS fix value")).addColorBarTitles(gpsFixQualityNames); 252 refScale = ColorScale.createCyclicScale(1).addTitle(tr("GPS Ref-ID")); 253 dateScale = ColorScale.createHSBScale(256).addTitle(tr("Track date")); 254 directionScale = ColorScale.createCyclicScale(256).setIntervalCount(4).addTitle(tr("Direction [°]")); 249 255 250 256 systemOfMeasurementChanged(null, null); … … 254 260 public void systemOfMeasurementChanged(String oldSoM, String newSoM) { 255 261 SystemOfMeasurement som = SystemOfMeasurement.getSystemOfMeasurement(); 256 velocityScale.addTitle(tr("Velocity , {0}", som.speedName));262 velocityScale.addTitle(tr("Velocity [{0}]", som.speedName)); 257 263 layer.invalidate(); 258 264 } … … 623 629 minval = interval.getStart().getEpochSecond(); 624 630 maxval = interval.getEnd().getEpochSecond(); 631 this.minTime = minval; 632 this.maxTime = maxval; 633 625 634 dateScale.setRange(minval, maxval); 626 635 } … … 642 651 Collections.sort(refs); 643 652 String[] a = {}; 644 refScale = ColorScale.createCyclicScale(refs.size()).addTitle(tr("GPS ref ")).addColorBarTitles(refs.toArray(a));653 refScale = ColorScale.createCyclicScale(refs.size()).addTitle(tr("GPS ref ID")).addColorBarTitles(refs.toArray(a)); 645 654 refScale.setRange(0, refs.size()); 646 655 } … … 1619 1628 1620 1629 if (colored == ColorMode.HDOP) { 1621 hdopScale.drawColorBar(g, w- 30, 50, 20, 100, 1.0);1630 hdopScale.drawColorBar(g, w-10, 50, 20, 100, 1.0); 1622 1631 } else if (colored == ColorMode.QUALITY) { 1623 qualityScale.drawColorBar(g, w- 30, 50, 20, 100, 1.0);1632 qualityScale.drawColorBar(g, w-10, 50, 20, 100, 1.0); 1624 1633 } else if (colored == ColorMode.FIX) { 1625 fixScale.drawColorBar(g, w- 30, 50, 20, 175, 1.0);1634 fixScale.drawColorBar(g, w-10, 50, 20, 175, 1.0); 1626 1635 } else if (colored == ColorMode.REF) { 1627 refScale.drawColorBar(g, w- 30, 50, 20, 175, 1.0);1636 refScale.drawColorBar(g, w-10, 50, 20, 175, 1.0); 1628 1637 } else if (colored == ColorMode.VELOCITY) { 1629 1638 SystemOfMeasurement som = SystemOfMeasurement.getSystemOfMeasurement(); 1630 velocityScale.drawColorBar(g, w- 30, 50, 20, 100, som.speedValue);1639 velocityScale.drawColorBar(g, w-10, 50, 20, 100, som.speedValue); 1631 1640 } else if (colored == ColorMode.DIRECTION) { 1632 directionScale.drawColorBar(g, w-30, 50, 20, 100, 180.0/Math.PI); 1641 directionScale.drawColorBar(g, w-10, 50, 20, 100, 180.0/Math.PI); 1642 } else if (colored == ColorMode.TIME) { 1643 dateScale.drawColorBarTime(g, w-10, 50, 20, 100, this.minTime, this.maxTime); 1633 1644 } 1634 1645 } -
trunk/src/org/openstreetmap/josm/tools/ColorHelper.java
r16319 r19236 74 74 public static Color getForegroundColor(Color bg) { 75 75 // http://stackoverflow.com/a/3943023/2257172 76 return bg == null ? null : 77 (bg.getRed()*0.299 + bg.getGreen()*0.587 + bg.getBlue()*0.114) > 186 ? 78 Color.BLACK : Color.WHITE; 76 if (bg == null) { 77 return null; 78 } 79 if (calculateContrastRatio(Color.WHITE, bg) > calculateContrastRatio(Color.BLACK, bg)) { 80 return Color.WHITE; 81 } 82 return Color.BLACK; 79 83 } 80 84 … … 129 133 return new Color(255 - clr.getRed(), 255 - clr.getGreen(), 255 - clr.getBlue(), clr.getAlpha()); 130 134 } 135 136 /** 137 * Calculate the relative "luminance" of a color. This is mostly useful for choosing background/foreground colours 138 * @see <a href="https://stackoverflow.com/questions/9733288/how-to-programmatically-calculate-the-contrast-ratio-between-two-colors"> 139 * constrast ratio</a> 140 */ 141 private static double calculateLuminance(Color color) { 142 final double rs = color.getRed() / 255.0; 143 final double gs = color.getGreen() / 255.0; 144 final double bs = color.getBlue() / 255.0; 145 final double r = calculateLuminanceStepFunction(rs); 146 final double g = calculateLuminanceStepFunction(gs); 147 final double b = calculateLuminanceStepFunction(bs); 148 return 0.2126 * r + 0.7152 * g + 0.0722 * b; 149 } 150 151 /** 152 * This is a step function for {@link #calculateLuminance(Color)} 153 * @param color The color to get the values for 154 * @return The value to use when calculating relative luminance 155 */ 156 private static double calculateLuminanceStepFunction(double color) { 157 if (color <= 0.03928) { 158 return color / 12.92; 159 } 160 return Math.pow((color + 0.055) / 1.055, 2.4); 161 } 162 163 /** 164 * Calculate the contrast between two colors (e.g. {@link Color#black} and {@link Color#white}). 165 * @param first The first color to use 166 * @param second The second color to use 167 * @return The contrast ratio ((L1 + 0.05)/(L2 + 0.05)) 168 * @since 19236 169 */ 170 public static double calculateContrastRatio(Color first, Color second) { 171 final double fL = calculateLuminance(first); 172 final double sL = calculateLuminance(second); 173 return (Math.max(fL, sL) + 0.05) / (Math.min(fL, sL) + 0.05); 174 } 131 175 } -
trunk/src/org/openstreetmap/josm/tools/ColorScale.java
r18801 r19236 1 1 // License: GPL. For details, see LICENSE file. 2 2 package org.openstreetmap.josm.tools; 3 4 import static org.openstreetmap.josm.tools.I18n.marktr; 3 5 4 6 import java.awt.Color; 5 7 import java.awt.FontMetrics; 6 8 import java.awt.Graphics2D; 9 import java.awt.Font; 7 10 import java.util.Arrays; 11 import java.util.Date; 12 import java.time.ZoneId; 13 import java.time.Instant; 14 import java.time.ZonedDateTime; 15 import java.time.format.DateTimeFormatter; 16 17 import org.openstreetmap.josm.data.preferences.NamedColorProperty; 18 19 import javax.swing.UIManager; 8 20 9 21 /** … … 12 24 */ 13 25 public final class ColorScale { 26 private static final Color LEGEND_BACKGROUND = new NamedColorProperty(marktr("gpx legend background"), new Color(180, 180, 180, 160)).get(); 27 private static final Color LEGEND_TEXT_OUTLINE_DARK = new NamedColorProperty(marktr("gpx legend text outline dark"), 28 new Color(102, 102, 102)).get(); 29 private static final Color LEGEND_TEXT_OUTLINE_BRIGHT = new NamedColorProperty(marktr("gpx legend text outline bright"), 30 new Color(204, 204, 204)).get(); 31 private static final Color LEGEND_TITLE = new NamedColorProperty(marktr("gpx legend title color"), new Color(0, 0, 0)).get(); 32 33 private static final String DAY_TIME_FORMAT = "yyyy-MM-dd HH:mm"; 34 private static final String TIME_FORMAT = "HH:mm:ss"; 35 /** Padding for the legend (from the text to the edge of the rectangle) */ 36 private static final byte PADDING = 19; 37 14 38 private double min, max; 15 39 private Color noDataColor; … … 226 250 227 251 /** 252 * draws an outline for the legend texts 253 * @param g The graphics to draw on 254 * @param txt The text to draw the outline 255 * @param x Text x 256 * @param y Text y 257 * @param color The color of the text 258 */ 259 private void drawOutline(final Graphics2D g, final String txt, final int x, final int y, final Color color) { 260 if (ColorHelper.calculateContrastRatio(color, LEGEND_TEXT_OUTLINE_DARK) >= 261 ColorHelper.calculateContrastRatio(color, LEGEND_TEXT_OUTLINE_BRIGHT)) { 262 g.setColor(LEGEND_TEXT_OUTLINE_DARK); 263 } else { 264 g.setColor(LEGEND_TEXT_OUTLINE_BRIGHT); 265 } 266 267 g.drawString(txt, x -1, y -1); 268 g.drawString(txt, x +1, y -1); 269 g.drawString(txt, x -1, y +1); 270 g.drawString(txt, x +1, y +1); 271 g.setColor(color); 272 } 273 274 /** 228 275 * Draws a color bar representing this scale on the given graphics 229 276 * @param g The graphics to draw on … … 234 281 * @param valueScale The scale factor of the values 235 282 */ 236 public void drawColorBar(Graphics2D g, int x, int y, int w, int h, double valueScale) { 237 int n = colors.length; 238 for (int i = 0; i < n; i++) { 239 g.setColor(colors[i]); 240 if (w < h) { 241 g.fillRect(x, y+i*h/n, w, h/n+1); 242 } else { 243 g.fillRect(x+i*w/n, y, w/n+1, h); 244 } 245 } 246 247 int fw, fh; 248 FontMetrics fm = g.getFontMetrics(); 249 fh = fm.getHeight()/2; 283 public void drawColorBar(final Graphics2D g, final int x, final int y, final int w, final int h, final double valueScale) { 284 final int n = colors.length; 285 286 final FontMetrics fm = calculateFontMetrics(g); 287 288 g.setColor(LEGEND_BACKGROUND); 289 290 // color bar texts width & height 291 final int fw; 292 final int fh = fm.getHeight() / 2; 293 294 // calculates the width of the color bar texts 250 295 if (colorBarTitles != null && colorBarTitles.length > 0) { 251 296 fw = Arrays.stream(colorBarTitles).mapToInt(fm::stringWidth).max().orElse(50); 252 297 } else { 253 298 fw = fm.stringWidth( … … 255 300 + fm.stringWidth("0.123"); 256 301 } 302 303 // background rectangle 304 final int[] t = drawBackgroundRectangle(g, x, y, w, h, fw, fh, fm.stringWidth(title)); 305 final int xRect = t[0]; 306 final int rectWidth = t[1]; 307 final int xText = t[2]; 308 final int titleWidth = t[3]; 309 310 // colorbar 311 for (int i = 0; i < n; i++) { 312 g.setColor(colors[i]); 313 if (w < h) { 314 double factor = n == 6 ? 1.2 : 1.07 + (0.045 * Math.log(n)); 315 if (n < 200) { 316 g.fillRect(xText + fw + PADDING / 3, y - PADDING / 2 + i * (int) ((double) h / n * factor), 317 w, (int) ((double) h / n * factor)); 318 } else { 319 g.fillRect(xText + fw + PADDING / 3, y - PADDING / 2 + i * h / (int) (n * 0.875), w, (h / n + 1)); 320 } 321 } else { 322 g.fillRect(xText + fw + 7 + i * w / n, y, w / n, h + 1); 323 } 324 } 325 326 // legend title 327 if (title != null) { 328 g.setColor(LEGEND_TITLE); 329 g.drawString(title, xRect + rectWidth / 2 - titleWidth / 2, y - fh * 3 / 2 - 10); 330 } 331 332 // legend texts 333 drawLegend(g, y, w, h, valueScale, fh, fw, xText); 334 257 335 g.setColor(noDataColor); 336 } 337 338 /** 339 * Draws a color bar representing the time scale on the given graphics 340 * @param g The graphics to draw on 341 * @param x Rect x 342 * @param y Rect y 343 * @param w Color bar width 344 * @param h Color bar height 345 * @param minVal start time of the track 346 * @param maxVal end time of the track 347 */ 348 public void drawColorBarTime(final Graphics2D g, final int x, final int y, final int w, final int h, 349 final double minVal, final double maxVal) { 350 final int n = colors.length; 351 352 final FontMetrics fm = calculateFontMetrics(g); 353 354 g.setColor(LEGEND_BACKGROUND); 355 356 final int padding = PADDING; 357 358 // color bar texts width & height 359 final int fw; 360 final int fh = fm.getHeight() / 2; 361 362 // calculates the width of the colorbar texts 363 if (maxVal - minVal > 86400) { 364 fw = fm.stringWidth(DAY_TIME_FORMAT); 365 } else { 366 fw = fm.stringWidth(TIME_FORMAT); 367 } 368 369 // background rectangle 370 final int[] t = drawBackgroundRectangle(g, x, y, w, h, fw, fh, fm.stringWidth(title)); 371 final int xRect = t[0]; 372 final int rectWidth = t[1]; 373 final int xText = t[2]; 374 final int titleWidth = t[3]; 375 376 // colorbar 377 for (int i = 0; i < n; i++) { 378 g.setColor(colors[i]); 379 if (w < h) { 380 g.fillRect(xText + fw + padding / 3, y - padding / 2 + i * h / (int) (n * 0.875), w, (h / n + 1)); 381 } else { 382 g.fillRect(xText + fw + padding / 3 + i * w / n, y, w / n + 1, h); 383 } 384 } 385 386 // legend title 258 387 if (title != null) { 259 g.drawString(title, x-fw-3, y-fh*3/2); 260 } 388 g.setColor(LEGEND_TITLE); 389 g.drawString(title, xRect + rectWidth / 2 - titleWidth / 2, y - fh * 3 / 2 - padding / 2); 390 } 391 392 // legend texts 393 drawTimeLegend(g, y, x, h, minVal, maxVal, fh, fw, xText); 394 395 g.setColor(noDataColor); 396 } 397 398 private static FontMetrics calculateFontMetrics(final Graphics2D g) { 399 final Font newFont = UIManager.getFont("PopupMenu.font"); 400 g.setFont(newFont); 401 return g.getFontMetrics(); 402 } 403 404 /** 405 * Draw the background rectangle 406 * @param g The graphics to draw on 407 * @param x Rect x 408 * @param y Rect y 409 * @param w Color bar width 410 * @param h Color bar height 411 * @param fw The font width 412 * @param fh The font height 413 * @param titleWidth The width of the title 414 * @return an @{code int[]} of [xRect, rectWidth, xText, titleWidth] TODO investigate using records in Java 17 415 */ 416 private int[] drawBackgroundRectangle(final Graphics2D g, final int x, final int y, 417 final int w, final int h, final int fw, final int fh, 418 int titleWidth) { 419 final int xRect; 420 final int rectWidth; 421 final int xText; 422 final int arcWidth = 20; 423 final int arcHeight = 20; 424 if (fw + w > titleWidth) { 425 rectWidth = w + fw + PADDING * 2; 426 xRect = x - rectWidth; 427 xText = xRect + (int) (PADDING / 1.2); 428 g.fillRoundRect(xRect, (fh * 3 / 2), rectWidth, h + y - (fh * 3 / 2) + (int) (PADDING / 1.5), arcWidth, arcHeight); 429 } else { 430 if (titleWidth >= 120) { 431 titleWidth = 120; 432 } 433 rectWidth = w + titleWidth + PADDING + PADDING / 2; 434 xRect = x - rectWidth; 435 xText = xRect + PADDING / 2 + rectWidth / 2 - fw; 436 g.fillRoundRect(xRect, (fh * 3 / 2), rectWidth, h + y - (fh * 3 / 2) + (int) (PADDING / 1.5), arcWidth, arcHeight); 437 } 438 return new int[] {xRect, rectWidth, xText, titleWidth}; 439 } 440 441 /** 442 * Draws the legend for the color bar representing the time scale on the given graphics 443 * @param g The graphics to draw on 444 * @param y Rect y 445 * @param w Color bar width 446 * @param h Color bar height 447 * @param fw The font width 448 * @param fh The font height 449 * @param valueScale The scale factor of the values 450 * @param xText The location to start drawing the text (x-axis) 451 */ 452 private void drawLegend(final Graphics2D g, final int y, final int w, final int h, final double valueScale, 453 final int fh, final int fw, final int xText) { 261 454 for (int i = 0; i <= intervalCount; i++) { 262 g.setColor(colors[(int) (1.0*i*n/intervalCount-1e-10)]); 263 String txt; 455 final String txt; 456 final Color color = colors[(int) (1.0 * i * colors.length / intervalCount - 1e-10)]; 457 g.setColor(color); 458 264 459 if (colorBarTitles != null && i < colorBarTitles.length) { 265 460 txt = colorBarTitles[i]; … … 268 463 txt = String.format("%.3f", val*valueScale); 269 464 } 270 if (intervalCount == 0) { 271 g.drawString(txt, x-fw-3, y+h/2+fh/2); 272 } else if (w < h) { 273 g.drawString(txt, x-fw-3, y+i*h/intervalCount+fh/2); 465 drawLegendText(g, y, w, h, fh, fw, xText, i, color, txt); 466 } 467 } 468 469 /** 470 * Draws the legend for the color bar representing the time scale on the given graphics 471 * @param g The graphics to draw on 472 * @param y Rect y 473 * @param w Color bar width 474 * @param h Color bar height 475 * @param minVal start time of the track 476 * @param maxVal end time of the track 477 * @param fw The font width 478 * @param fh The font height 479 * @param xText The location to start drawing the text (x-axis) 480 */ 481 private void drawTimeLegend(final Graphics2D g, final int y, final int w, final int h, 482 final double minVal, final double maxVal, 483 final int fh, final int fw, final int xText) { 484 for (int i = 0; i <= intervalCount; i++) { 485 final String txt; 486 final Color color = colors[(int) (1.0 * i * colors.length / intervalCount - 1e-10)]; 487 g.setColor(color); 488 489 if (colorBarTitles != null && i < colorBarTitles.length) { 490 txt = colorBarTitles[i]; 274 491 } else { 275 g.drawString(txt, x+i*w/intervalCount-fw/2, y+fh-3); 492 final double val = minVal + i * (maxVal - minVal) / intervalCount; 493 final long longval = (long) val; 494 495 final Date date = new Date(longval * 1000L); 496 final Instant dateInst = date.toInstant(); 497 498 final ZoneId gmt = ZoneId.of("GMT"); 499 final ZonedDateTime zonedDateTime = dateInst.atZone(gmt); 500 501 String formatted; 502 503 if (maxVal-minVal > 86400) { 504 final DateTimeFormatter day = DateTimeFormatter.ofPattern(DAY_TIME_FORMAT); 505 formatted = zonedDateTime.format(day); 506 } else { 507 final DateTimeFormatter time = DateTimeFormatter.ofPattern(TIME_FORMAT); 508 formatted = zonedDateTime.format(time); 509 } 510 511 txt = formatted; 276 512 } 513 drawLegendText(g, y, w, h, fh, fw, xText, i, color, txt); 514 } 515 } 516 517 /** 518 * Draws the legend for the color bar representing the time scale on the given graphics 519 * @param g The graphics to draw on 520 * @param y Rect y 521 * @param w Color bar width 522 * @param h Color bar height 523 * @param fw The font width 524 * @param fh The font height 525 * @param xText The location to start drawing the text (x-axis) 526 * @param color The color of the text to draw 527 * @param txt The text string to draw 528 * @param i The index of the legend (so we can calculate the y location) 529 */ 530 private void drawLegendText(Graphics2D g, int y, int w, int h, int fh, int fw, int xText, 531 int i, Color color, String txt) { 532 533 if (intervalCount == 0) { 534 drawOutline(g, txt, xText, y + h / 2 + fh / 2, color); 535 g.drawString(txt, xText, y + h / 2 + fh / 2); 536 } else if (w < h) { 537 drawOutline(g, txt, xText, y + i * h / intervalCount + fh / 2, color); 538 g.drawString(txt, xText, y + i * h / intervalCount + fh / 2); 539 } else { 540 final int xLoc = xText + i * w / intervalCount - fw / 2 - (int) (PADDING / 1.3); 541 final int yLoc = y + fh - 5; 542 drawOutline(g, txt, xLoc, yLoc, color); 543 g.drawString(txt, xLoc, yLoc); 277 544 } 278 545 }
Note:
See TracChangeset
for help on using the changeset viewer.