commonMain.jetbrains.datalore.plot.config.GeomProtoClientSide.kt Maven / Gradle / Ivy
/*
* Copyright (c) 2019. JetBrains s.r.o.
* Use of this source code is governed by the MIT license that can be found in the LICENSE file.
*/
package jetbrains.datalore.plot.config
import jetbrains.datalore.base.geometry.DoubleRectangle
import jetbrains.datalore.base.geometry.DoubleVector
import jetbrains.datalore.base.spatial.projections.identity
import jetbrains.datalore.base.spatial.projections.mercator
import jetbrains.datalore.base.stringFormat.StringFormat
import jetbrains.datalore.plot.base.Aes
import jetbrains.datalore.plot.base.GeomKind
import jetbrains.datalore.plot.base.geom.*
import jetbrains.datalore.plot.base.stat.DotplotStat
import jetbrains.datalore.plot.builder.assemble.geom.GeomProvider
import jetbrains.datalore.plot.builder.coord.CoordProvider
import jetbrains.datalore.plot.builder.coord.CoordProviders
import jetbrains.datalore.plot.config.Option.Geom.Boxplot
import jetbrains.datalore.plot.config.Option.Geom.BoxplotOutlier
import jetbrains.datalore.plot.config.Option.Geom.AreaRidges
import jetbrains.datalore.plot.config.Option.Geom.CrossBar
import jetbrains.datalore.plot.config.Option.Geom.Dotplot
import jetbrains.datalore.plot.config.Option.Geom.Image
import jetbrains.datalore.plot.config.Option.Geom.Label
import jetbrains.datalore.plot.config.Option.Geom.Path
import jetbrains.datalore.plot.config.Option.Geom.Pie
import jetbrains.datalore.plot.config.Option.Geom.Point
import jetbrains.datalore.plot.config.Option.Geom.PointRange
import jetbrains.datalore.plot.config.Option.Geom.Segment
import jetbrains.datalore.plot.config.Option.Geom.Step
import jetbrains.datalore.plot.config.Option.Geom.Text
import jetbrains.datalore.plot.config.Option.Geom.Violin
import jetbrains.datalore.plot.config.Option.Geom.YDotplot
import jetbrains.datalore.plot.config.Option.Layer.USE_CRS
class GeomProtoClientSide(geomKind: GeomKind) : GeomProto(geomKind) {
fun preferredCoordinateSystem(layerConfig: LayerConfig): CoordProvider? {
return when (geomKind) {
GeomKind.TILE,
GeomKind.BIN_2D,
GeomKind.CONTOUR,
GeomKind.CONTOURF,
GeomKind.DENSITY2D,
GeomKind.DENSITY2DF,
GeomKind.RASTER,
GeomKind.IMAGE -> CoordProviders.fixed(1.0)
GeomKind.MAP -> CoordProviders.map(projection = identity().takeIf { layerConfig.has(USE_CRS) } ?: mercator())
else -> null
}
}
fun geomProvider(opts: OptionsAccessor): GeomProvider {
when (geomKind) {
GeomKind.DOT_PLOT -> return GeomProvider.dotplot {
val geom = DotplotGeom()
if (opts.hasOwn(Dotplot.DOTSIZE)) {
geom.dotSize = opts.getDouble(Dotplot.DOTSIZE)!!
}
if (opts.hasOwn(Dotplot.STACKRATIO)) {
geom.stackRatio = opts.getDouble(Dotplot.STACKRATIO)!!
}
if (opts.hasOwn(Dotplot.STACKGROUPS)) {
geom.stackGroups = opts.getBoolean(Dotplot.STACKGROUPS)
}
if (opts.hasOwn(Dotplot.STACKDIR)) {
geom.stackDir = DotplotGeom.Stackdir.safeValueOf(opts.getString(Dotplot.STACKDIR)!!)
}
if (opts.hasOwn(Dotplot.METHOD)) {
geom.method = DotplotStat.Method.safeValueOf(opts.getString(Dotplot.METHOD)!!)
}
geom
}
GeomKind.CROSS_BAR -> return GeomProvider.crossBar {
val geom = CrossBarGeom()
if (opts.hasOwn(CrossBar.FATTEN)) {
geom.fattenMidline = opts.getDouble(CrossBar.FATTEN)!!
}
geom
}
GeomKind.POINT_RANGE -> return GeomProvider.pointRange {
val geom = PointRangeGeom()
if (opts.hasOwn(PointRange.FATTEN)) {
geom.fattenMidPoint = opts.getDouble(PointRange.FATTEN)!!
}
geom
}
GeomKind.BOX_PLOT -> return GeomProvider.boxplot {
val geom = BoxplotGeom()
if (opts.hasOwn(Boxplot.FATTEN)) {
geom.fattenMidline = opts.getDouble(Boxplot.FATTEN)!!
}
if (opts.hasOwn(Boxplot.WHISKER_WIDTH)) {
geom.whiskerWidth = opts.getDouble(Boxplot.WHISKER_WIDTH)!!
}
if (opts.hasOwn(BoxplotOutlier.COLOR)) {
geom.outlierColor = opts.getColor(BoxplotOutlier.COLOR)!!
}
if (opts.hasOwn(BoxplotOutlier.FILL)) {
geom.outlierFill = opts.getColor(BoxplotOutlier.FILL)!!
}
geom.outlierShape = opts.getShape(BoxplotOutlier.SHAPE)
geom.outlierSize = opts.getDouble(BoxplotOutlier.SIZE)
geom
}
GeomKind.AREA_RIDGES -> return GeomProvider.arearidges {
val geom = AreaRidgesGeom()
if (opts.hasOwn(AreaRidges.SCALE)) {
geom.scale = opts.getDoubleDef(AreaRidges.SCALE, AreaRidgesGeom.DEF_SCALE)
}
if (opts.hasOwn(AreaRidges.MIN_HEIGHT)) {
geom.minHeight = opts.getDoubleDef(AreaRidges.MIN_HEIGHT, AreaRidgesGeom.DEF_MIN_HEIGHT)
}
if (opts.hasOwn(AreaRidges.QUANTILE_LINES)) {
geom.quantileLines = opts.getBoolean(AreaRidges.QUANTILE_LINES, AreaRidgesGeom.DEF_QUANTILE_LINES)
}
geom
}
GeomKind.VIOLIN -> return GeomProvider.violin {
val geom = ViolinGeom()
if (opts.hasOwn(Violin.DRAW_QUANTILES)) {
geom.setDrawQuantiles(opts.getBoundedDoubleList(Violin.DRAW_QUANTILES, 0.0, 1.0))
}
if (opts.hasOwn(Violin.SHOW_HALF)) {
geom.showHalf = opts.getDouble(Violin.SHOW_HALF)!!
}
geom
}
GeomKind.Y_DOT_PLOT -> return GeomProvider.ydotplot {
val geom = YDotplotGeom()
if (opts.hasOwn(YDotplot.DOTSIZE)) {
geom.dotSize = opts.getDouble(YDotplot.DOTSIZE)!!
}
if (opts.hasOwn(YDotplot.STACKRATIO)) {
geom.stackRatio = opts.getDouble(YDotplot.STACKRATIO)!!
}
if (opts.hasOwn(YDotplot.STACKGROUPS)) {
geom.stackGroups = opts.getBoolean(YDotplot.STACKGROUPS)
}
if (opts.hasOwn(YDotplot.STACKDIR)) {
geom.yStackDir = YDotplotGeom.YStackdir.safeValueOf(opts.getString(YDotplot.STACKDIR)!!)
}
if (opts.hasOwn(YDotplot.METHOD)) {
geom.method = DotplotStat.Method.safeValueOf(opts.getString(YDotplot.METHOD)!!)
}
geom
}
GeomKind.STEP -> return GeomProvider.step {
val geom = StepGeom()
if (opts.hasOwn(Step.DIRECTION)) {
geom.setDirection(opts.getString(Step.DIRECTION)!!)
}
geom
}
GeomKind.SEGMENT -> return GeomProvider.segment {
val geom = SegmentGeom()
if (opts.has(Segment.ARROW)) {
val cfg1 = ArrowSpecConfig.create(opts[Segment.ARROW]!!)
geom.arrowSpec = cfg1.createArrowSpec()
}
if (opts.has(Segment.ANIMATION)) {
geom.animation = opts[Segment.ANIMATION]
}
if (opts.has(Segment.FLAT)) {
geom.flat = opts.getBoolean(Segment.FLAT)
}
geom
}
GeomKind.PATH -> return GeomProvider.path {
val geom = PathGeom()
if (opts.has(Path.ANIMATION)) {
geom.animation = opts[Path.ANIMATION]
}
if (opts.has(Path.FLAT)) {
geom.flat = opts.getBoolean(Path.FLAT)
}
geom
}
GeomKind.POINT -> return GeomProvider.point {
val geom = PointGeom()
if (opts.has(Point.ANIMATION)) {
geom.animation = opts[Point.ANIMATION]
}
geom.sizeUnit = opts.getString(Point.SIZE_UNIT)?.lowercase()
geom
}
GeomKind.TEXT -> return GeomProvider.text {
val geom = TextGeom()
applyTextOptions(opts, geom)
geom
}
GeomKind.LABEL -> return GeomProvider.label {
val geom = LabelGeom()
applyTextOptions(opts, geom)
opts.getDouble(Label.LABEL_PADDING)?.let { geom.paddingFactor = it }
opts.getDouble(Label.LABEL_R)?.let { geom.radiusFactor = it }
opts.getDouble(Label.LABEL_SIZE)?.let { geom.borderWidth = it }
geom
}
GeomKind.IMAGE -> return GeomProvider.image {
require(opts.hasOwn(Image.HREF)) { "Image reference URL (href) is not specified." }
for (s in listOf(Image.XMIN, Image.XMAX, Image.YMIN, Image.YMAX)) {
require(opts.hasOwn(s)) { "'$s' is not specified." }
}
ImageGeom(
imageUrl = opts.getString(Image.HREF)!!,
bbox = DoubleRectangle.span(
DoubleVector(opts.getDoubleSafe(Image.XMIN), opts.getDoubleSafe(Image.YMIN)),
DoubleVector(opts.getDoubleSafe(Image.XMAX), opts.getDoubleSafe(Image.YMAX)),
)
)
}
GeomKind.PIE -> return GeomProvider.pie {
val geom = PieGeom()
opts.getDouble(Pie.HOLE)?.let { geom.holeSize = it }
opts.getDouble(Pie.STROKE)?.let { geom.strokeWidth = it }
opts.getColor(Pie.STROKE_COLOR)?.let { geom.strokeColor = it }
if (opts.has(Pie.FILL_BY)) {
val fillBy = opts.getString(Pie.FILL_BY)!!
val aes = Option.Mapping.toAes(fillBy)
geom.fillWithColor = aes == Aes.COLOR
}
geom
}
else -> {
require(PROVIDER.containsKey(geomKind)) { "Provider doesn't support geom kind: '$geomKind'" }
return PROVIDER[geomKind]!!
}
}
}
private companion object {
private val PROVIDER = HashMap()
init {
PROVIDER[GeomKind.POINT] = GeomProvider.point()
PROVIDER[GeomKind.PATH] = GeomProvider.path()
PROVIDER[GeomKind.LINE] = GeomProvider.line()
PROVIDER[GeomKind.SMOOTH] = GeomProvider.smooth()
PROVIDER[GeomKind.BAR] = GeomProvider.bar()
PROVIDER[GeomKind.HISTOGRAM] = GeomProvider.histogram()
// dotplot - special case
PROVIDER[GeomKind.TILE] = GeomProvider.tile()
PROVIDER[GeomKind.BIN_2D] = GeomProvider.bin2d()
PROVIDER[GeomKind.ERROR_BAR] = GeomProvider.errorBar()
// crossbar - special case
// pointrange - special case
PROVIDER[GeomKind.LINE_RANGE] = GeomProvider.lineRange()
PROVIDER[GeomKind.CONTOUR] = GeomProvider.contour()
PROVIDER[GeomKind.CONTOURF] = GeomProvider.contourf()
PROVIDER[GeomKind.POLYGON] = GeomProvider.polygon()
PROVIDER[GeomKind.MAP] = GeomProvider.map()
PROVIDER[GeomKind.AB_LINE] = GeomProvider.abline()
PROVIDER[GeomKind.H_LINE] = GeomProvider.hline()
PROVIDER[GeomKind.V_LINE] = GeomProvider.vline()
// boxplot - special case
// area ridges - special case
// violin - special case
PROVIDER[GeomKind.RIBBON] = GeomProvider.ribbon()
PROVIDER[GeomKind.AREA] = GeomProvider.area()
PROVIDER[GeomKind.DENSITY] = GeomProvider.density()
PROVIDER[GeomKind.DENSITY2D] = GeomProvider.density2d()
PROVIDER[GeomKind.DENSITY2DF] = GeomProvider.density2df()
PROVIDER[GeomKind.JITTER] = GeomProvider.jitter()
PROVIDER[GeomKind.Q_Q] = GeomProvider.qq()
PROVIDER[GeomKind.Q_Q_2] = GeomProvider.qq2()
PROVIDER[GeomKind.Q_Q_LINE] = GeomProvider.qqline()
PROVIDER[GeomKind.Q_Q_2_LINE] = GeomProvider.qq2line()
PROVIDER[GeomKind.FREQPOLY] = GeomProvider.freqpoly()
// step - special case
PROVIDER[GeomKind.RECT] = GeomProvider.rect()
// segment - special case
// text, label - special case
PROVIDER[GeomKind.RASTER] = GeomProvider.raster()
// image - special case
// pie - special case
PROVIDER[GeomKind.LIVE_MAP] = GeomProvider.livemap()
}
private fun applyTextOptions(opts: OptionsAccessor, geom: TextGeom) {
opts.getString(Text.LABEL_FORMAT)?.let { geom.formatter = StringFormat.forOneArg(it)::format }
opts.getString(Text.NA_TEXT)?.let { geom.naValue = it }
geom.sizeUnit = opts.getString(Text.SIZE_UNIT)?.lowercase()
}
}
}