Example #1
0
  private static float calculateMin(Number[] yValues) {
    float min = yValues[0].floatValue();
    for (Number d : yValues) {
      min = Math.min(min, d.floatValue());
    }

    return min;
  }
Example #2
0
  private static float calculateMax(Number[] yValues) {
    float max = yValues[0].floatValue();
    for (Number d : yValues) {
      max = Math.max(max, d.floatValue());
    }

    return max;
  }
Example #3
0
  /**
   * Draw a sparkline with x axis and y axis values, and pre-calculated min and max of the y axis
   * values. Use this when you already have min/max values calculated and want to avoid extra
   * calculations by <code>SparklineGraph</code> X values <b>must</b> be ordered and not repeated.
   *
   * @param xValues X axis values
   * @param yValues Y axis values
   * @param yMinValue Minimum value of the Y axis, should be correct
   * @param yMaxValue Maximum value of the Y axis, should be correct
   * @param parameters Rendering parameters
   * @return Image of the sparkline
   */
  public static BufferedImage draw(
      Number[] xValues,
      Number[] yValues,
      Number yMinValue,
      Number yMaxValue,
      SparklineParameters parameters) {
    if (parameters == null) {
      throw new IllegalArgumentException("parameters can't be null");
    }

    if (yValues == null || yValues.length < 2) {
      throw new IllegalArgumentException("Y values can't be null and have a length of at least 2");
    }

    if (xValues != null && xValues.length != yValues.length) {
      throw new IllegalArgumentException("X values should have the same length as Y values");
    }

    final BufferedImage image =
        new BufferedImage(
            parameters.getWidth(), parameters.getHeight(), BufferedImage.TYPE_INT_ARGB);
    final Color backgroundColor =
        parameters.getBackgroundColor() != null
            ? parameters.getBackgroundColor()
            : SparklineParameters.DEFAULT_BACKGROUND_COLOR;
    final Color lineColor =
        parameters.getLineColor() != null
            ? parameters.getLineColor()
            : SparklineParameters.DEFAULT_LINE_COLOR;
    final Color areaColor =
        parameters.getAreaColor() != null
            ? parameters.getAreaColor()
            : SparklineParameters.DEFAULT_AREA_COLOR;
    Color highlightMinColor = parameters.getHighlightMinColor();
    Color highlightMaxColor = parameters.getHighlightMaxColor();
    int width = parameters.getWidth();
    int height = parameters.getHeight();
    ArrayList<HighlightParameters> highlightsList = new ArrayList<>();
    Color highlightValueColor =
        parameters.getHighligtValueColor() != null
            ? parameters.getHighligtValueColor()
            : SparklineParameters.DEFAULT_HIGHLIGHT_VALUE_COLOR;
    Integer highlightedValueXPosition = parameters.getHighlightedValueXPosition();
    String highlightedValueText = null;

    // Calculate y min if not provided:
    float yMin;
    if (yMinValue != null) {
      yMin = yMinValue.floatValue();
    } else {
      yMin = calculateMin(yValues);
    }

    // Calculate y max if not provided:
    float yMax;
    if (yMaxValue != null) {
      yMax = yMaxValue.floatValue();
    } else {
      yMax = calculateMax(yValues);
    }

    // Begin drawing:
    final Graphics2D g = image.createGraphics();
    g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    // Background:
    if (backgroundColor != null && !parameters.isTransparentBackground()) {
      g.setBackground(backgroundColor);
      g.clearRect(0, 0, width, height);
    }

    // Sparklines:
    g.setPaint(lineColor);
    // For special case, all the Y values are equal:
    if (yMin == yMax) {
      // Disable min/max highlight since all Y values are equal
      highlightMaxColor = null;
      highlightMinColor = null;
    }

    if (highlightMaxColor != null || highlightMinColor != null || highlightValueColor != null) {
      height -=
          HIGHLIGHT_RADIUS; // Highlight circle radius pixels less in order not to draw outside
      // bounds
      width -= HIGHLIGHT_RADIUS; // Highlight circle radius pixels less in order not to draw outside
      // bounds
      g.translate(HIGHLIGHT_RADIUS / 2, HIGHLIGHT_RADIUS / 2);
    }

    // Poly for area:
    Path2D.Float path = new Path2D.Float();
    path.moveTo(0, height);

    // Calculate x tick width and x min value:
    float xMin;
    float xTickWidth;
    if (xValues == null) {
      xMin = 0;
      xTickWidth = (float) width / (yValues.length - 1);
    } else {
      xMin = xValues[0].floatValue(); // X values have to be ordered
      xTickWidth = (float) width / (xValues[xValues.length - 1].floatValue() - xMin);
    }
    // Calculate yTickWidth:
    float yTickWidth = height / (yMax - yMin);

    float bottom = height;

    float x0, y0, x1, y1;
    for (int i = 0; i < yValues.length - 1; i++) {
      if (xValues == null) {
        x0 = i * xTickWidth;
        x1 = (i + 1) * xTickWidth;
      } else {
        x0 = (xValues[i].floatValue() - xMin) * xTickWidth;
        x1 = (xValues[i + 1].floatValue() - xMin) * xTickWidth;
      }

      if (yMin == yMax) {
        // For special case, all the Y values are equal:
        y0 = y1 = bottom - height / 2.0f;
      } else {
        y0 = bottom - (yValues[i].floatValue() - yMin) * yTickWidth;
        y1 = bottom - (yValues[i + 1].floatValue() - yMin) * yTickWidth;
      }

      // Draw line:
      g.draw(new Line2D.Double(x0, y0, x1, y1));

      // Add to path for area:
      if (parameters.isDrawArea()) {
        if (i == 0) {
          path.lineTo(x0, y0);
        }
        path.lineTo(x1, y1);
      }

      // Save min/max values highlihgting if enabled:
      if (yValues[i + 1].floatValue() == yMin && highlightMinColor != null) {
        highlightsList.add(new HighlightParameters(x1, y1, highlightMinColor));
        highlightMinColor = null; // Only highlight first min
      }
      if (yValues[i + 1].floatValue() == yMax && highlightMaxColor != null) {
        highlightsList.add(new HighlightParameters(x1, y1, highlightMaxColor));
        highlightMaxColor = null; // Only highlight first max
      }
      // Save first point special case min/max values highlihgting if enabled:
      if (i == 0) {
        if (yValues[0].floatValue() == yMin && highlightMinColor != null) {
          highlightsList.add(new HighlightParameters(x0, y0, highlightMinColor));
          highlightMinColor = null; // Only highlight first min
        }
        if (yValues[0].floatValue() == yMax && highlightMaxColor != null) {
          highlightsList.add(new HighlightParameters(x0, y0, highlightMaxColor));
          highlightMaxColor = null; // Only highlight first max
        }
      }

      // Save x position value highlighting if enabled:
      if (highlightedValueXPosition != null && x1 >= highlightedValueXPosition) {
        // Highlight closest point:
        if ((highlightedValueXPosition - x0) < (x1 - x0) / 2f) { // Highlight left point
          highlightsList.add(new HighlightParameters(x0, y0, highlightValueColor));
          if (parameters.getHighlightTextColor() != null) {
            highlightedValueText =
                buildHighlightText(
                    parameters.getHighlightTextMode(),
                    xValues != null ? xValues[i] : i,
                    yValues[i]);
          }
        } else { // Highlight right point
          highlightsList.add(new HighlightParameters(x1, y1, highlightValueColor));
          if (parameters.getHighlightTextColor() != null) {
            highlightedValueText =
                buildHighlightText(
                    parameters.getHighlightTextMode(),
                    xValues != null ? xValues[i + 1] : i + 1,
                    yValues[i + 1]);
          }
        }
        highlightedValueXPosition = null;
      }
    }

    // Draw area
    if (parameters.isDrawArea()) {
      // End Path
      path.lineTo(width, height);
      path.lineTo(0, height);

      g.setColor(areaColor);
      g.fill(path);
    }

    // Draw list of highlights at the end in order to always draw them on top of lines:
    for (HighlightParameters p : highlightsList) {
      drawHighlight(g, p.x, p.y, p.color);
    }

    // Finally draw value text (x,y) if enabled:
    if (highlightedValueText != null) {
      g.setFont(g.getFont().deriveFont(Font.BOLD));
      int textWidth = g.getFontMetrics().stringWidth(highlightedValueText);
      int textHeight = g.getFontMetrics().getHeight();
      g.translate((width - textWidth) / 2f, (height + textHeight / 2f) / 2f);
      // Draw bounding box if enabled:
      if (parameters.getHighlightTextBoxColor() != null) {
        g.setPaint(parameters.getHighlightTextBoxColor());
        g.fill(g.getFontMetrics().getStringBounds(highlightedValueText, g));
      }
      g.setPaint(parameters.getHighlightTextColor());
      g.drawString(highlightedValueText, 0, 0);
    }

    return image;
  }