commonMain.jetbrains.datalore.plot.builder.assemble.GeomLayerBuilder.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of lets-plot-common Show documentation
Show all versions of lets-plot-common Show documentation
Lets-Plot JVM package without rendering part
/*
* Copyright (c) 2020. 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.builder.assemble
import jetbrains.datalore.base.typedKey.TypedKeyHashMap
import jetbrains.datalore.plot.base.*
import jetbrains.datalore.plot.base.aes.AestheticsDefaults
import jetbrains.datalore.plot.base.data.DataFrameUtil
import jetbrains.datalore.plot.base.data.TransformVar
import jetbrains.datalore.plot.base.geom.LiveMapGeom
import jetbrains.datalore.plot.base.geom.LiveMapProvider
import jetbrains.datalore.plot.base.interact.ContextualMapping
import jetbrains.datalore.plot.base.interact.GeomTargetLocator.LookupSpec
import jetbrains.datalore.plot.base.interact.MappedDataAccess
import jetbrains.datalore.plot.base.render.LegendKeyElementFactory
import jetbrains.datalore.plot.base.stat.SimpleStatContext
import jetbrains.datalore.plot.base.stat.Stats
import jetbrains.datalore.plot.builder.GeomLayer
import jetbrains.datalore.plot.builder.PosProviderContext
import jetbrains.datalore.plot.builder.VarBinding
import jetbrains.datalore.plot.builder.assemble.geom.GeomProvider
import jetbrains.datalore.plot.builder.assemble.geom.PointDataAccess
import jetbrains.datalore.plot.builder.data.DataProcessing
import jetbrains.datalore.plot.builder.data.GroupingContext
import jetbrains.datalore.plot.builder.interact.ContextualMappingProvider
import jetbrains.datalore.plot.builder.scale.ScaleProvider
class GeomLayerBuilder {
private val myBindings = ArrayList()
private val myConstantByAes = TypedKeyHashMap()
private lateinit var myStat: Stat
private lateinit var myPosProvider: PosProvider
private lateinit var myGeomProvider: GeomProvider
private var myGroupingVarName: String? = null
private var myPathIdVarName: String? = null
private val myScaleProviderByAes = HashMap, ScaleProvider<*>>()
private var myDataPreprocessor: ((DataFrame, TypedScaleMap) -> DataFrame)? = null
private var myLocatorLookupSpec: LookupSpec = LookupSpec.NONE
private var myContextualMappingProvider: ContextualMappingProvider = ContextualMappingProvider.NONE
private var myIsLegendDisabled: Boolean = false
fun stat(v: Stat): GeomLayerBuilder {
myStat = v
return this
}
fun pos(v: PosProvider): GeomLayerBuilder {
myPosProvider = v
return this
}
fun geom(v: GeomProvider): GeomLayerBuilder {
myGeomProvider = v
return this
}
fun addBinding(v: VarBinding): GeomLayerBuilder {
myBindings.add(v)
return this
}
fun groupingVar(v: DataFrame.Variable): GeomLayerBuilder {
myGroupingVarName = v.name
return this
}
fun groupingVarName(v: String): GeomLayerBuilder {
myGroupingVarName = v
return this
}
fun pathIdVarName(v: String): GeomLayerBuilder {
myPathIdVarName = v
return this
}
fun addConstantAes(aes: Aes, v: T): GeomLayerBuilder {
myConstantByAes.put(aes, v)
return this
}
fun addScaleProvider(aes: Aes, scaleProvider: ScaleProvider): GeomLayerBuilder {
myScaleProviderByAes[aes] = scaleProvider
return this
}
fun locatorLookupSpec(v: LookupSpec): GeomLayerBuilder {
myLocatorLookupSpec = v
return this
}
fun contextualMappingProvider(v: ContextualMappingProvider): GeomLayerBuilder {
myContextualMappingProvider = v
return this
}
fun disableLegend(v: Boolean): GeomLayerBuilder {
myIsLegendDisabled = v
return this
}
fun build(data: DataFrame, scaleMap: TypedScaleMap): GeomLayer {
@Suppress("NAME_SHADOWING")
var data = data
if (myDataPreprocessor != null) {
data = myDataPreprocessor!!(data, scaleMap)
}
// make sure 'original' series are transformed
data = DataProcessing.transformOriginals(data, myBindings, scaleMap)
val replacementBindings = HashMap(
// No 'origin' variables beyond this point.
// Replace all 'origin' variables in bindings with 'transform' variables
myBindings.map {
it.aes to if (it.variable.isOrigin) {
val transformVar = DataFrameUtil.transformVarFor(it.aes)
VarBinding(transformVar, it.aes)
} else {
it
}
}.toMap()
)
// add 'transform' variable for each 'stat' variable
val bindingsToPut = ArrayList()
for (binding in replacementBindings.values) {
val variable = binding.variable
if (variable.isStat) {
val aes = binding.aes
val scale = scaleMap[aes]
data = DataFrameUtil.applyTransform(data, variable, aes, scale)
bindingsToPut.add(VarBinding(TransformVar.forAes(aes), aes))
}
}
// replace 'stat' vars with 'transform' vars in bindings
for (binding in bindingsToPut) {
replacementBindings[binding.aes] = binding
}
// (!) Positional aes scales have undefined `mapper` at this time because
// dimensions of plot are not yet known.
// Data Access shouldn't use aes mapper (!)
val dataAccess = PointDataAccess(data, replacementBindings, scaleMap)
return MyGeomLayer(
data,
myGeomProvider,
myPosProvider,
myGeomProvider.renders(),
GroupingContext(data, myBindings, myGroupingVarName, myPathIdVarName, handlesGroups()).groupMapper,
replacementBindings.values,
myConstantByAes,
scaleMap,
dataAccess,
myLocatorLookupSpec,
myContextualMappingProvider.createContextualMapping(dataAccess, data),
myIsLegendDisabled
)
}
private fun handlesGroups(): Boolean {
return myGeomProvider.handlesGroups() || myPosProvider.handlesGroups()
}
private class MyGeomLayer(
override val dataFrame: DataFrame,
geomProvider: GeomProvider,
private val myPosProvider: PosProvider,
renderedAes: List>,
override val group: (Int) -> Int,
varBindings: Collection,
constantByAes: TypedKeyHashMap,
override val scaleMap: TypedScaleMap,
override val dataAccess: MappedDataAccess,
override val locatorLookupSpec: LookupSpec,
override val contextualMapping: ContextualMapping,
override val isLegendDisabled: Boolean
) : GeomLayer {
override val geom: Geom = geomProvider.createGeom()
override val geomKind: GeomKind = geomProvider.geomKind
override val aestheticsDefaults: AestheticsDefaults
private val myRenderedAes: List>
private val myConstantByAes: TypedKeyHashMap
private val myVarBindingsByAes = HashMap, VarBinding>()
override val legendKeyElementFactory: LegendKeyElementFactory
get() = geom.legendKeyElementFactory
override val isLiveMap: Boolean
get() = geom is LiveMapGeom
init {
myRenderedAes = ArrayList(renderedAes)
// constant value by aes (default + specified)
aestheticsDefaults = geomProvider.aestheticsDefaults()
myConstantByAes = TypedKeyHashMap()
for (key in constantByAes.keys()) {
myConstantByAes.put(key, constantByAes[key])
}
for (varBinding in varBindings) {
myVarBindingsByAes[varBinding.aes] = varBinding
}
}
override fun renderedAes(): List> {
return myRenderedAes
}
override fun createPos(ctx: PosProviderContext): PositionAdjustment {
return myPosProvider.createPos(ctx)
}
override fun hasBinding(aes: Aes<*>): Boolean {
return myVarBindingsByAes.containsKey(aes)
}
override fun getBinding(aes: Aes): VarBinding {
return myVarBindingsByAes[aes]!!
}
override fun hasConstant(aes: Aes<*>): Boolean {
return myConstantByAes.containsKey(aes)
}
override fun getConstant(aes: Aes): T {
require(hasConstant(aes)) { "Constant value is not defined for aes $aes" }
return myConstantByAes[aes]
}
override fun getDefault(aes: Aes): T {
return aestheticsDefaults.defaultValue(aes)
}
override fun rangeIncludesZero(aes: Aes<*>): Boolean {
return aestheticsDefaults.rangeIncludesZero(aes)
}
override fun setLiveMapProvider(liveMapProvider: LiveMapProvider) {
if (geom is LiveMapGeom) {
geom.setLiveMapProvider(liveMapProvider)
} else {
throw IllegalStateException("Not Livemap: " + geom::class.simpleName)
}
}
}
companion object {
fun demoAndTest(): GeomLayerBuilder {
val builder = GeomLayerBuilder()
builder.myDataPreprocessor = { data, scaleMap ->
val transformedData = DataProcessing.transformOriginals(data, builder.myBindings, scaleMap)
when (val stat = builder.myStat) {
Stats.IDENTITY -> transformedData
else -> {
val statCtx = SimpleStatContext(transformedData)
val groupingContext =
GroupingContext(
transformedData,
builder.myBindings,
builder.myGroupingVarName,
builder.myPathIdVarName,
true
)
val dataAndGroupingContext = DataProcessing.buildStatData(
transformedData,
stat,
builder.myBindings,
scaleMap,
groupingContext,
PlotFacets.undefined(),
statCtx,
varsWithoutBinding = emptyList(),
orderOptions = emptyList(),
aggregateOperation = null,
::println
)
dataAndGroupingContext.data
}
}
}
return builder
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy