g2inR / g2r

experiments with R and g2
Other
33 stars 3 forks source link

spec-based functionality #5

Open timelyportfolio opened 6 years ago

timelyportfolio commented 6 years ago

I somehow completely missed the documentation on spec-based chart creation in g2 https://antv.alipay.com/zh-cn/g2/3.x/api/options.html. This should greatly simplify #1 and possibly helps make a decision on #2. @mdequeljoe, I think our best option will be to pursue this route.

timelyportfolio commented 6 years ago

JavaScript code is dramatically simplified and R API can focus on building the spec as a list.

#devtools::install_github("g2inr/g2r")
library(g2r)
library(htmltools)

#  gosh this is ugly and I'm sure highly worst practice
asJSON <- jsonlite:::asJSON
setMethod("asJSON", "ANY", function(x, force = FALSE, ...) {
  if (inherits(x, "noquote")) {
    return(x)
  }
  if (isS4(x) && !is(x, "classRepresentation")) {
    if (isTRUE(force)) {
      return(asJSON(attributes(x), force = force, ...))
    } else {
      stop("No method for S4 class:", class(x))
    }
  } else if (length(class(x)) > 1) {
    # If an object has multiple classes, we recursively try the next class. This is
    # S3 style dispatching that doesn't work by default for formal method definitions
    # There should be a more native way to accomplish this
    return(asJSON(structure(x, class = class(x)[-1]), force = force, ...))
  } else if (isTRUE(force) && existsMethod("asJSON", class(unclass(x)))) {
    # As a last resort we can force encoding using the unclassed object
    return(asJSON(unclass(x), force = force, ...))
  } else if (isTRUE(force)) {
    return(asJSON(NULL))
    warning("No method asJSON S3 class: ", class(x))
  } else {
    # If even that doesn't work, we give up.
    stop("No method asJSON S3 class: ", class(x))
  }
})
assignInNamespace("asJSON", asJSON, ns="jsonlite")
rm(asJSON, envir = .GlobalEnv)

######## using spec-based api https://antv.alipay.com/zh-cn/g2/3.x/api/options.html
spec_template <- "
G2.track(false)
var data = %s

var chart_config = %s;

var chart = new G2.Chart(chart_config);

chart.render()
"

g2cht1_spec <- list(
  container = "c1",
  forceFit = TRUE,
  height = 400, #noquote("window.innerHeight"),
  data = noquote("data"),
  options = list(
    scales = list(
      value = list(min = 0),
      year = list(range = c(0,1))
    ),
    tooltip = list(
      crosshairs = list(type="line")
    ),
    geoms = list(
      list(type = "line", position = "year*value"),
      list(
        type = "point",
        position = "year*value",
        size = 4,
        shape = "circle",
        style = list(
          stroke = "#fff",
          lineWidth = 1
        )
      )
    )
  )
)

scr <- tags$script(HTML(
  sprintf(
    spec_template
    ,
    "[
  { year: '1991', value: 3 },
  { year: '1992', value: 4 },
  { year: '1993', value: 3.5 },
  { year: '1994', value: 5 },
  { year: '1995', value: 4.9 },
  { year: '1996', value: 6 },
  { year: '1997', value: 7 },
  { year: '1998', value: 9 },
  { year: '1999', value: 13 }
]",
    jsonlite::toJSON(g2cht1_spec, auto_unbox=TRUE, pretty=TRUE)  
  )
))

browsable(
  tagList(
    dep_g2(),
    tags$div(id="c1"),
    scr
  )
)
mdequeljoe commented 6 years ago

@timelyportfolio good call I hadn't seen that either. I agree that this seems to be the best way forward

timelyportfolio commented 6 years ago

@mdequeljoe, I quickly sketched out a pipe-based approach to build the spec. I wonder how closely we want to resemble ggplot2. Here is an example.

mdequeljoe commented 6 years ago

@timelyportfolio thanks for sharing this sketch. Yes, it probably could be useful to align the API style to ggplot2 to the extent that this makes sense. Its seem like at the least -some of the familiar conventions from ggplot2 could be applied (declaring a default aesthetic once, facets default to fixed scales etc.). Maybe even consider using non-standard evaluation for variable names. Also think some middle ground should be found in terms of adhering to G2’s syntax as much as possible – this way the API documentation for G2 would more or less correspond to the R API.