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

smile.plot.vega.VegaLite Maven / Gradle / Ivy

There is a newer version: 4.0.0
Show newest version
/*
 * Copyright (c) 2010-2021 Haifeng Li. All rights reserved.
 *
 * Smile is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Smile is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Smile.  If not, see .
 */
package smile.plot.vega;

import java.awt.*;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.format.DateTimeFormatter;
import java.util.UUID;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import smile.util.Strings;

/**
 * Vega-Lite specifications are JSON objects that describe a diverse range
 * of interactive visualizations. Besides using a single view specification
 * as a standalone visualization, Vega-Lite also provides operators for
 * composing multiple view specifications into a layered or multi-view
 * specification. These operators include layer, facet, concat, and repeat.
 *
 * @author Haifeng Li
 */
public class VegaLite {
    private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(VegaLite.class);
    /**
     * The schema of Vega-Lite.
     */
    private static final String schema = "https://vega.github.io/schema/vega-lite/v5.json";
    /**
     * The MIME type of Vega-Lite.
     */
    private static final String mime = "application/vnd.vegalite.v5+json";
    /**
     * ISO 8601 format.
     */
    static final DateTimeFormatter ISO8601 = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSX");
    /**
     * JSON object mapping.
     */
    static final ObjectMapper mapper = new ObjectMapper();
    /**
     * The Vega-Lite specification.
     */
    final ObjectNode spec = mapper.createObjectNode();
    /**
     * The configuration object lists configuration properties
     * of a visualization for creating a consistent theme.
     */
    final ObjectNode config = spec.putObject("config");
    /**
     * Default properties for single view plots.
     */
    final ObjectNode view = config.putObject("view");

    /**
     * Constructor.
     */
    public VegaLite() {
        spec.put("$schema", schema);
        view.put("continuousWidth", 400);
        view.put("continuousHeight", 400);
    }

    @Override
    public String toString() {
        return spec.toString();
    }

    /**
     * Returns the specification in pretty print.
     * @return the specification in pretty print.
     */
    public String toPrettyString() {
        return spec.toPrettyString();
    }

    /**
     * Returns the Vega-Lite specification.
     */
    public ObjectNode spec() {
        return spec;
    }

    // ====== Properties of Top-Level Specifications ======
    /**
     * Returns the configuration object that lists properties of
     * a visualization for creating a consistent theme. This property
     * can only be defined at the top-level of a specification.
     */
    public Config config() {
        return new Config(config);
    }

    /**
     * Returns the configuration object defining the style of
     * a single view visualization.
     */
    public ViewConfig viewConfig() {
        return new ViewConfig(view);
    }

    /**
     * Optional metadata that will be passed to Vega. This object is completely
     * ignored by Vega and Vega-Lite and can be used for custom metadata.
     */
    public VegaLite usermeta(JsonNode metadata) {
        spec.set("usermeta", metadata);
        return this;
    }

    /**
     * Optional metadata that will be passed to Vega. This object is completely
     * ignored by Vega and Vega-Lite and can be used for custom metadata.
     */
    public VegaLite usermeta(Object metadata) {
        spec.putPOJO("usermeta", metadata);
        return this;
    }

    /**
     * Sets the background of the entire view with CSS color property.
     */
    public VegaLite background(String color) {
        spec.put("background", color);
        return this;
    }

    /**
     * Specifies padding for all sides.
     * The visualization padding, in pixels, is from the edge of the
     * visualization canvas to the data rectangle.
     */
    public VegaLite padding(int size) {
        spec.put("padding", size);
        return this;
    }

    /**
     * Specifies padding for each side.
     * The visualization padding, in pixels, is from the edge of the
     * visualization canvas to the data rectangle.
     */
    public VegaLite padding(int left, int top, int right, int bottom) {
        ObjectNode padding = spec.putObject("padding");
        padding.put("left", left);
        padding.put("top", top);
        padding.put("right", right);
        padding.put("bottom", bottom);
        return this;
    }

    /**
     * Sets the overall size of the visualization. The total size of
     * a Vega-Lite visualization may be determined by multiple factors:
     * specified width, height, and padding values, as well as content
     * such as axes, legends, and titles.
     */
    public VegaLite autosize() {
        return autosize("pad", false, "content");
    }

    /**
     * Sets the overall size of the visualization. The total size of
     * a Vega-Lite visualization may be determined by multiple factors:
     * specified width, height, and padding values, as well as content
     * such as axes, legends, and titles.
     *
     * @param type     The sizing format type. One of "pad", "fit", "fit-x",
     *                 "fit-y", or "none". See Vega-Lite documentation for
     *                 descriptions of each.
     * @param resize   A boolean flag indicating if autosize layout should
     *                 be re-calculated on every view update.
     * @param contains Determines how size calculation should be performed,
     *                 one of "content" or "padding". The default setting
     *                 ("content") interprets the width and height settings
     *                 as the data rectangle (plotting) dimensions, to which
     *                 padding is then added. In contrast, the "padding"
     *                 setting includes the padding within the view size
     *                 calculations, such that the width and height settings
     *                 indicate the total intended size of the view.
     * @link https://vega.github.io/vega-lite/docs/size.html#autosize
     */
    public VegaLite autosize(String type, boolean resize, String contains) {
        ObjectNode autosize = spec.putObject("autosize");
        autosize.put("type", type);
        autosize.put("resize", resize);
        autosize.put("contains", contains);
        return this;
    }

    // ====== Common Properties ======

    /**
     * Sets the name of the visualization for later reference.
     */
    public VegaLite name(String name) {
        spec.put("name", name);
        return this;
    }

    /**
     * Sets the description of this mark for commenting purpose.
     */
    public VegaLite description(String description) {
        spec.put("description", description);
        return this;
    }

    /**
     * Sets a descriptive title to a chart.
     * @param title a descriptive title.
     * @return this object.
     */
    public VegaLite title(String title) {
        spec.put("title", title);
        return this;
    }

    /**
     * Returns the data specification object.
     * @return the data specification object.
     */
    public Data data() {
        Data data = new Data();
        spec.set("data", data.spec);
        return data;
    }

    /**
     * Returns the data transformation object.
     * such as filter and new field
     * calculation. Data transformations in Vega-Lite are described
     * via either view-level transforms (the transform property) or
     * field transforms inside encoding (bin, timeUnit, aggregate,
     * sort, and stack).
     * 

* When both types of transforms are specified, the view-level * transforms are executed first based on the order in the * array. Then the inline transforms are executed in this order: * bin, timeUnit, aggregate, sort, and stack. * @return the data transformation object. */ public Transform transform() { ArrayNode node = spec.has("transform") ? (ArrayNode) spec.get("transform") : spec.putArray("transform"); return new Transform(node); } /** * Displays the plot with the default browser. */ public void show() throws IOException, HeadlessException { show(false); } /** * Displays the plot with the default browser. * @param silent If true, silently swallow any exception. */ public void show(boolean silent) throws IOException, HeadlessException { try { Path path = Files.createTempFile("smile-plot-", ".html"); path.toFile().deleteOnExit(); Files.write(path, html().getBytes(StandardCharsets.UTF_8)); Desktop.getDesktop().browse(path.toUri()); } catch (Exception ex) { if (silent) { logger.warn("Failed to show " + this.getClass().getSimpleName(), ex); } else { throw ex; } } } /** * Returns the HTML of plot specification with Vega Embed. */ public String html() throws JsonProcessingException { return html("en"); } /** * Returns the HTML of plot specification with Vega Embed. * @param lang the primary language of document. * @return the HTML of plot specification with Vega Embed. */ public String html(String lang) throws JsonProcessingException { String title = spec.has("title") ? spec.get("title").asText() : "Smile Plot"; return String.format(""" %s

""", lang, title, spec.toPrettyString()); } /** * Returns the HTML wrapped in an iframe to render in notebooks. */ public String iframe() throws JsonProcessingException { return iframe(UUID.randomUUID().toString()); } /** * Returns the HTML wrapped in an iframe to render in notebooks. * * @param id the iframe HTML id. */ public String iframe(String id) throws JsonProcessingException { String src = Strings.htmlEscape(html()); return String.format(""" """, id, src, id); } /** Returns the encoding object. */ ObjectNode encoding() { return spec.has("encoding") ? (ObjectNode) spec.get("encoding") : spec.putObject("encoding"); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy