All Downloads are FREE. Search and download functionalities are using the official Maven repository.

tech.tablesaw.plotly.components.Axis Maven / Gradle / Ivy

The newest version!
package tech.tablesaw.plotly.components;

import static tech.tablesaw.plotly.components.Axis.Spikes.SpikeSnap.DATA;

import com.google.common.base.Preconditions;
import java.util.HashMap;
import java.util.Map;
import tech.tablesaw.plotly.Utils;
import tech.tablesaw.plotly.traces.ScatterTrace;

public class Axis extends Component {

  public enum CategoryOrder {
    TRACE("trace"),
    CATEGORY_ASCENDING("category ascending"),
    CATEGORY_DESCENDING("category descending"),
    ARRAY("array");

    private final String value;

    CategoryOrder(String value) {
      this.value = value;
    }

    @Override
    public String toString() {
      return value;
    }
  }

  /**
   * Sets the axis type. By default, plotly attempts to determined the axis type by looking into the
   * data of the traces that referenced the axis in question.
   */
  public enum Type {
    LINEAR("linear"),
    LOG("log"),
    DATE("date"),
    CATEGORY("category"),
    DEFAULT("-");

    private final String value;

    Type(String value) {
      this.value = value;
    }

    @Override
    public String toString() {
      return value;
    }
  }

  /**
   * Determines whether or not the range of this axis is computed in relation to the input data. See
   * `rangemode` for more info. If `range` is provided, then `autorange` is set to "False".
   */
  public enum AutoRange {
    TRUE("true"),
    FALSE("false"),
    REVERSED("reversed");

    private final String value;

    AutoRange(String value) {
      this.value = value;
    }

    @Override
    public String toString() {
      return value;
    }
  }

  /**
   * If this axis needs to be compressed (either due to its own `scaleanchor` and `scaleratio` or
   * those of the other axis), determines how that happens: by increasing the "range" (default), or
   * by decreasing the "domain".
   */
  public enum Constrain {
    RANGE("range"),
    DOMAIN("domain");

    private final String value;

    Constrain(String value) {
      this.value = value;
    }

    @Override
    public String toString() {
      return value;
    }
  }

  /**
   * If this axis needs to be compressed (either due to its own `scaleanchor` and `scaleratio` or
   * those of the other axis), determines which direction we push the originally specified plot
   * area. Options are "left", "center" (default), and "right" for x axes, and "top", "middle"
   * (default), and "bottom" for y axes.
   */
  public enum ConstrainToward {
    LEFT("left"),
    CENTER("center"),
    RIGHT("right"),
    TOP("top"),
    MIDDLE("middle"),
    BOTTOM("bottom");

    private final String value;

    ConstrainToward(String value) {
      this.value = value;
    }

    @Override
    public String toString() {
      return value;
    }
  }

  /**
   * If set to another axis id (e.g. `x2`, `y`), the range of this axis changes together with the
   * range of the corresponding axis such that the scale of pixels per unit is in a constant ratio.
   * Both axes are still zoomable, but when you zoom one, the other will zoom the same amount,
   * keeping a fixed midpoint. `constrain` and `constraintoward` determine how we enforce the
   * constraint. You can chain these, ie `yaxis: {scaleanchor: "x"}, xaxis2: {scaleanchor: "y"}` but
   * you can only link axes of the same `type`. The linked axis can have the opposite letter (to
   * constrain the aspect ratio) or the same letter (to match scales across subplots). Loops
   * (`yaxis: {scaleanchor: "x"}, xaxis: {scaleanchor: "y"}` or longer) are redundant and the last
   * constraint encountered will be ignored to avoid possible inconsistent constraints via
   * `scaleratio`.
   *
   * 

TODO: Just make this a string? */ public enum ScaleAnchor { X("/^x([2-9]|[1-9][0-9]+)?$/"), Y("/^y([2-9]|[1-9][0-9]+)?$/"); private final String value; ScaleAnchor(String value) { this.value = value; } @Override public String toString() { return value; } } /** * If "normal", the range is computed in relation to the extrema of the input data. If "tozero"`, * the range extends to 0, regardless of the input data If "nonnegative", the range is * non-negative, regardless of the input data. */ public enum RangeMode { NORMAL("normal"), TO_ZERO("tozero"), NON_NEGATIVE("nonnegative"); private final String value; RangeMode(String value) { this.value = value; } @Override public String toString() { return value; } } /** * Determines whether an x (y) axis is positioned at the "bottom" ("left") or "top" ("right") of * the plotting area. */ public enum Side { left("left"), // DEFAULT right("right"), top("top"), bottom("bottom"); private final String value; Side(String value) { this.value = value; } @Override public String toString() { return value; } } private static final String DEFAULT_COLOR = "#444"; private static final String DEFAULT_ZERO_LINE_COLOR = "#444"; private static final String DEFAULT_LINE_COLOR = "#444"; private static final String DEFAULT_GRID_COLOR = "#eee"; private static final int DEFAULT_LINE_WIDTH = 1; private static final int DEFAULT_ZERO_LINE_WIDTH = 1; private static final int DEFAULT_GRID_WIDTH = 1; private static final boolean DEFAULT_SHOW_LINE = true; private static final boolean DEFAULT_SHOW_GRID = true; private static final boolean DEFAULT_ZERO_LINE = false; private static final double DEFAULT_SCALE_RATIO = 1.0; private static final Constrain DEFAULT_CONSTRAIN_RANGE = Constrain.RANGE; private static final AutoRange DEFAULT_AUTO_RANGE = AutoRange.TRUE; private static final Type DEFAULT_TYPE = Type.DEFAULT; private static final boolean DEFAULT_VISIBLE = true; private final String title; private final boolean visible; private final String color; private final Font font; private final Font titleFont; private final Type type; private final RangeMode rangeMode; private final AutoRange autoRange; private final Object[] range; private final boolean fixedRange; // true means the axis cannot be zoomed private final Constrain constrain; private final ConstrainToward constrainToward; private final double scaleRatio; private final Spikes spikes; private final int lineWidth; private final int zeroLineWidth; private final int gridWidth; private final String lineColor; private final String zeroLineColor; private final String gridColor; private final boolean showLine; private final boolean zeroLine; private final boolean showGrid; private final Side side; private final ScatterTrace.YAxis overlaying; private final CategoryOrder categoryOrder; private final TickSettings tickSettings; private final float[] domain; private final String rangeSlider; public static AxisBuilder builder() { return new AxisBuilder(); } private Axis(AxisBuilder builder) { title = builder.title; titleFont = builder.titleFont; type = builder.type; visible = builder.visible; color = builder.color; font = builder.font; autoRange = builder.autoRange; range = builder.range; rangeMode = builder.rangeMode; fixedRange = builder.fixedRange; tickSettings = builder.tickSettings; side = builder.side; overlaying = builder.overlaying; spikes = builder.spikes; showLine = builder.showLine; zeroLine = builder.zeroLine; showGrid = builder.showGrid; lineColor = builder.lineColor; zeroLineColor = builder.zeroLineColor; gridColor = builder.gridColor; lineWidth = builder.lineWidth; zeroLineWidth = builder.zeroLineWidth; gridWidth = builder.gridWidth; constrain = builder.constrain; constrainToward = builder.constrainToward; scaleRatio = builder.scaleRatio; categoryOrder = builder.categoryOrder; domain = builder.domain; rangeSlider = builder.rangeSlider; } @Override public String asJavascript() { return asJavascript("axis_template.html"); } @Override protected Map getContext() { Map context = new HashMap<>(); context.put("title", title); context.put("titleFont", titleFont); if (visible != DEFAULT_VISIBLE) context.put("visible", visible); if (!type.equals(DEFAULT_TYPE)) context.put("type", type); if (!color.equals(DEFAULT_COLOR)) context.put("color", color); if (font != null) { context.put("font", font); } if (side != null) { context.put("side", side); } if (overlaying != null) { context.put("overlaying", overlaying); } if (!autoRange.equals(DEFAULT_AUTO_RANGE)) context.put("autoRange", autoRange); context.put("rangeMode", rangeMode); if (range != null) { context.put("range", Utils.dataAsString(range)); } context.put("fixedRange", fixedRange); if (scaleRatio != DEFAULT_SCALE_RATIO) context.put("scaleRatio", scaleRatio); if (!constrain.equals(DEFAULT_CONSTRAIN_RANGE)) context.put("constrain", constrain); if (constrainToward != null) { context.put("constrainToward", constrainToward); } if (spikes != null) { spikes.updateContext(context); } if (tickSettings != null) { tickSettings.updateContext(context); } if (categoryOrder != null) { context.put("categoryOrder", categoryOrder); } if (gridWidth != DEFAULT_GRID_WIDTH) context.put("gridWidth", gridWidth); if (lineWidth != DEFAULT_LINE_WIDTH) context.put("lineWidth", lineWidth); if (zeroLineWidth != DEFAULT_ZERO_LINE_WIDTH) context.put("zeroLineWidth", zeroLineWidth); if (!lineColor.equals(DEFAULT_LINE_COLOR)) context.put("lineColor", lineColor); if (!zeroLineColor.equals(DEFAULT_ZERO_LINE_COLOR)) context.put("zeroLineColor", zeroLineColor); if (!gridColor.equals(DEFAULT_GRID_COLOR)) context.put("gridColor", gridColor); if (showLine != DEFAULT_SHOW_LINE) context.put("showLine", showLine); if (zeroLine != DEFAULT_ZERO_LINE) context.put("zeroLine", zeroLine); if (showGrid != DEFAULT_SHOW_GRID) context.put("showGrid", showGrid); if (domain != null) { context.put("domain", String.format("[%.2f, %.2f]", domain[0], domain[1])); } if (rangeSlider != null) { context.put("rangeslider", rangeSlider); } return context; } public static class AxisBuilder { private Constrain constrain = DEFAULT_CONSTRAIN_RANGE; private ConstrainToward constrainToward; private double scaleRatio = DEFAULT_SCALE_RATIO; private Font titleFont; private String title = ""; private boolean visible = DEFAULT_VISIBLE; private String color = DEFAULT_COLOR; private Font font; private Side side; private Type type = DEFAULT_TYPE; private RangeMode rangeMode = RangeMode.NORMAL; private AutoRange autoRange = DEFAULT_AUTO_RANGE; private Object[] range; private boolean fixedRange = true; // true means the axis cannot be zoomed private TickSettings tickSettings; private Spikes spikes = null; private boolean showLine = DEFAULT_SHOW_LINE; private String lineColor = DEFAULT_LINE_COLOR; private int lineWidth = DEFAULT_LINE_WIDTH; private boolean zeroLine = DEFAULT_ZERO_LINE; private String zeroLineColor = DEFAULT_ZERO_LINE_COLOR; private int zeroLineWidth = DEFAULT_ZERO_LINE_WIDTH; private boolean showGrid = DEFAULT_SHOW_GRID; private String gridColor = DEFAULT_GRID_COLOR; private int gridWidth = DEFAULT_GRID_WIDTH; private ScatterTrace.YAxis overlaying; private CategoryOrder categoryOrder; private float[] domain = null; private String rangeSlider = null; private AxisBuilder() {} public AxisBuilder title(String title) { this.title = title; return this; } public AxisBuilder titleFont(Font titleFont) { this.titleFont = titleFont; return this; } public AxisBuilder type(Type type) { this.type = type; return this; } public AxisBuilder categoryOrder(CategoryOrder categoryOrder) { this.categoryOrder = categoryOrder; return this; } public AxisBuilder domain(float start, float end) { this.domain = new float[] {start, end}; return this; } public AxisBuilder visible(boolean visible) { this.visible = visible; return this; } public AxisBuilder side(Side side) { this.side = side; return this; } /** * Instructs plotly to overly the trace with this axis on top of a trace with another axis * * @param axisToOverlay The axis we want to overlay * @return this AxisBuilder */ public AxisBuilder overlaying(ScatterTrace.YAxis axisToOverlay) { this.overlaying = axisToOverlay; return this; } /** Determines whether or not this axis is zoom-able. If True, then zoom is disabled. */ public AxisBuilder fixedRange(boolean fixedRange) { this.fixedRange = fixedRange; return this; } public AxisBuilder color(String color) { this.color = color; return this; } public AxisBuilder font(Font font) { this.font = font; return this; } /** * If "normal", the range is computed in relation to the extrema of the input data. If * "tozero"`, the range extends to 0, regardless of the input data If "nonnegative", the range * is non-negative, regardless of the input data. * *

The default is normal. */ public AxisBuilder rangeMode(RangeMode rangeMode) { this.rangeMode = rangeMode; return this; } public AxisBuilder spikes(Spikes spikes) { this.spikes = spikes; return this; } /** * Determines whether or not the range of this axis is computed in relation to the input data. * See `rangemode` for more info. If `range` is provided, then `autorange` is set to "False". */ public AxisBuilder autoRange(AutoRange autoRange) { this.autoRange = autoRange; if (range != null && autoRange != AutoRange.FALSE) { throw new IllegalArgumentException( "Can't set autoRange to anything but FALSE after specifying a range."); } return this; } /** * Sets the range of this axis. If the axis `type` is "log", then you must take the log of your * desired range (e.g. to set the range from 1 to 100, set the range from 0 to 2). * *

If the axis `type` is "date", it should be date strings, like date data, though Date * objects and unix milliseconds will be accepted and converted to strings. If the axis `type` * is "category", it should be numbers, using the scale where each category is assigned a serial * number from zero in the order it appears. */ public AxisBuilder range(Object[] range) { this.range = range; this.autoRange = AutoRange.FALSE; return this; } /** * Sets the range of this axis. If the axis `type` is "log", then you must take the log of your * desired range (e.g. to set the range from 1 to 100, set the range from 0 to 2). * *

If the axis `type` is "date", it should be date strings, like date data, though Date * objects and unix milliseconds will be accepted and converted to strings. If the axis `type` * is "category", it should be numbers, using the scale where each category is assigned a serial * number from zero in the order it appears. */ public AxisBuilder range(Object low, Object high) { Object[] range = new Object[2]; range[0] = low; range[1] = high; this.range = range; this.autoRange = AutoRange.FALSE; return this; } public AxisBuilder constrain(Constrain constrain) { this.constrain = constrain; return this; } public AxisBuilder constrainToward(ConstrainToward constrainToward) { this.constrainToward = constrainToward; return this; } /** * If this axis is linked to another by `scaleanchor`, this determines the pixel to unit scale * ratio. For example, if this value is 10, then every unit on this axis spans 10 times the * number of pixels as a unit on the linked axis. Use this for example to create an elevation * profile where the vertical scale is exaggerated a fixed amount with respect to the * horizontal. * * @param scaleRatio a number >= 1 * @return this AxisBuilder */ public AxisBuilder scaleRatio(double scaleRatio) { Preconditions.checkArgument(scaleRatio >= 1.0); this.scaleRatio = scaleRatio; return this; } /** Defines all the settings related to the display of tick marks on this axis */ public AxisBuilder tickSettings(TickSettings tickSettings) { this.tickSettings = tickSettings; return this; } public AxisBuilder lineWidth(int lineWidth) { Preconditions.checkArgument(lineWidth >= 0); this.lineWidth = lineWidth; return this; } public AxisBuilder zeroLineWidth(int zeroLineWidth) { Preconditions.checkArgument(zeroLineWidth >= 0); this.zeroLineWidth = zeroLineWidth; return this; } public AxisBuilder gridWidth(int width) { Preconditions.checkArgument(width >= 0); this.gridWidth = width; return this; } public AxisBuilder lineColor(String color) { this.lineColor = color; return this; } public AxisBuilder gridColor(String color) { this.gridColor = color; return this; } public AxisBuilder zeroLineColor(String color) { this.zeroLineColor = color; return this; } public AxisBuilder showLine(boolean showLine) { this.showLine = showLine; return this; } public AxisBuilder showGrid(boolean showGrid) { this.showGrid = showGrid; return this; } public AxisBuilder showZeroLine(boolean zeroLine) { this.zeroLine = zeroLine; return this; } public AxisBuilder rangeslider(String slider) { this.rangeSlider = slider; return this; } public Axis build() { return new Axis(this); } } public static class Spikes { private final String color; private final int thickness; private final String dash; private final SpikeMode mode; private final SpikeSnap snap; private Spikes(SpikesBuilder builder) { this.color = builder.color; this.thickness = builder.thickness; this.dash = builder.dash; this.mode = builder.mode; this.snap = builder.snap; } private void updateContext(Map context) { context.put("showSpikes", true); context.put("spikeMode", mode); context.put("spikeThickness", thickness); context.put("spikeDash", dash); context.put("spikeColor", color); context.put("spikeSnap", snap); } public enum SpikeSnap { DATA("data"), CURSOR("cursor"); private final String value; SpikeSnap(String value) { this.value = value; } @Override public String toString() { return value; } } public enum SpikeMode { TO_AXIS("toaxis"), ACROSS("across"), MARKER("marker"), TO_AXIS_AND_ACROSS("toaxis+across"), TO_AXIS_AND_MARKER("toaxis+marker"), ACROSS_AND_MARKER("across+marker"), TO_AXIS_AND_ACROSS_AND_MARKER("toaxis+across+marker"); private final String value; SpikeMode(String value) { this.value = value; } @Override public String toString() { return value; } } public static SpikesBuilder builder() { return new SpikesBuilder(); } public static class SpikesBuilder { private String color = null; private int thickness = 3; private String dash = "dash"; private SpikeMode mode = SpikeMode.TO_AXIS; private SpikeSnap snap = DATA; private SpikesBuilder() {} public SpikesBuilder color(String color) { this.color = color; return this; } /** * Sets the dash style of lines. Set to a dash type string ("solid", "dot", "dash", * "longdash", "dashdot", or "longdashdot") or a dash length list in px (eg * "5px,10px,2px,2px"). */ public SpikesBuilder dash(String dash) { this.dash = dash; return this; } /** * Any combination of "toaxis", "across", "marker" examples: "toaxis", "across", * "toaxis+across", "toaxis+across+marker" default: "toaxis" */ public SpikesBuilder mode(SpikeMode mode) { this.mode = mode; return this; } /** * Determines whether spikelines are stuck to the cursor or to the closest datapoints. * default: DATA */ public SpikesBuilder snap(SpikeSnap snap) { this.snap = snap; return this; } /** Sets the width (in px) of the zero line. default: 3 */ public SpikesBuilder thickness(int thickness) { this.thickness = thickness; return this; } public Spikes build() { return new Spikes(this); } } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy