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

commonMain.jetbrains.datalore.plot.config.PlotConfig.kt Maven / Gradle / Ivy

There is a newer version: 4.5.3-alpha1
Show newest version
/*
 * 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.gcommon.base.Preconditions.checkArgument
import jetbrains.datalore.base.gcommon.base.Preconditions.checkState
import jetbrains.datalore.plot.base.Aes
import jetbrains.datalore.plot.base.DataFrame
import jetbrains.datalore.plot.base.data.DataFrameUtil
import jetbrains.datalore.plot.builder.assemble.PlotFacets
import jetbrains.datalore.plot.builder.assemble.TypedScaleMap
import jetbrains.datalore.plot.config.Option.Meta
import jetbrains.datalore.plot.config.Option.Meta.DATA_META
import jetbrains.datalore.plot.config.Option.Meta.Kind
import jetbrains.datalore.plot.config.Option.Plot.COORD
import jetbrains.datalore.plot.config.Option.Plot.FACET
import jetbrains.datalore.plot.config.Option.Plot.LAYERS
import jetbrains.datalore.plot.config.Option.Plot.SCALES
import jetbrains.datalore.plot.config.Option.Plot.TITLE
import jetbrains.datalore.plot.config.Option.Plot.TITLE_TEXT
import jetbrains.datalore.plot.config.Option.PlotBase.DATA
import jetbrains.datalore.plot.config.Option.PlotBase.MAPPING

abstract class PlotConfig(
    opts: Map
) : OptionsAccessor(opts, DEF_OPTIONS) {
    val layerConfigs: List
    val facets: PlotFacets

    val scaleMap: TypedScaleMap
    protected val scaleConfigs: List>

    protected var sharedData: DataFrame
        private set

    val title: String?
        get() = getMap(TITLE)[TITLE_TEXT] as String?

    protected open val isClientSide: Boolean
        get() = false

    val containsLiveMap: Boolean
        get() = layerConfigs.any(LayerConfig::isLiveMap)

    init {

        val (plotMappings, plotData) = DataMetaUtil.createDataFrame(
            options = this,
            commonData = DataFrame.Builder.emptyFrame(),
            commonDiscreteAes = emptySet(),
            commonMappings = emptyMap(),
            isClientSide = isClientSide
        )

        sharedData = plotData

        if (!isClientSide) {
            update(MAPPING, plotMappings)
        }

        layerConfigs = createLayerConfigs(sharedData)

        // build all scales
        scaleConfigs = createScaleConfigs(getList(SCALES) + DataMetaUtil.createScaleSpecs(opts))
        val scaleProvidersMap = PlotConfigUtil.createScaleProviders(scaleConfigs)

        scaleMap = PlotConfigUtil.createScales(layerConfigs, scaleProvidersMap, isClientSide)

        facets = if (has(FACET)) {
            val facetOptions = getMap(FACET)
            val facetConfig = FacetConfig(facetOptions)
            val dataByLayer = ArrayList()
            for (layerConfig in layerConfigs) {
                dataByLayer.add(layerConfig.combinedData)
            }
            facetConfig.createFacets(dataByLayer)
        } else {
            PlotFacets.undefined()
        }
    }

    fun createScaleConfigs(scaleOptionsList: List<*>): List> {
        // merge options by 'aes'
        val mergedOpts = HashMap, MutableMap>()
        for (opts in scaleOptionsList) {
            @Suppress("UNCHECKED_CAST")
            val optsMap = opts as Map
            val aes = ScaleConfig.aesOrFail(optsMap)
            if (!mergedOpts.containsKey(aes)) {
                mergedOpts[aes] = HashMap()
            }

            mergedOpts[aes]!!.putAll(optsMap)
        }

        val result = ArrayList>()
        for (scaleOptions in mergedOpts.values) {
            result.add(ScaleConfig(scaleOptions))
        }
        return result
    }

    private fun createLayerConfigs(
        sharedData: DataFrame
    ): List {

        val layerConfigs = ArrayList()
        val layerOptionsList = getList(LAYERS)
        for (layerOptions in layerOptionsList) {
            checkArgument(
                layerOptions is Map<*, *>,
                "Layer options: expected Map but was " + layerOptions!!::class.simpleName
            )
            @Suppress("UNCHECKED_CAST")
            val layerConfig = createLayerConfig(
                layerOptions as Map,
                sharedData,
                getMap(MAPPING),
                DataMetaUtil.getAsDiscreteAesSet(getMap(DATA_META))
            )
            layerConfigs.add(layerConfig)
        }
        return layerConfigs
    }

    protected abstract fun createLayerConfig(
        layerOptions: Map,
        sharedData: DataFrame,
        plotMappings: Map<*, *>,
        plotDiscreteAes: Set<*>
    ): LayerConfig


    protected fun replaceSharedData(plotData: DataFrame) {
        checkState(!isClientSide)   // This class is immutable on client-side
        sharedData = plotData
        update(DATA, DataFrameUtil.toMap(plotData))
    }

    companion object {
        private const val ERROR_MESSAGE = "__error_message"
        private val DEF_OPTIONS = mapOf(
            COORD to Option.CoordName.CARTESIAN
        )
        internal const val PLOT_COMPUTATION_MESSAGES = "computation_messages"

        fun failure(message: String): Map {
            return mapOf(ERROR_MESSAGE to message)
        }

        fun assertPlotSpecOrErrorMessage(opts: Map) {
            val identified = isFailure(opts) ||
                    isPlotSpec(opts) ||
                    isGGBunchSpec(opts)

            if (!identified) {
                throw IllegalArgumentException("Invalid root feature kind: absent or unsupported  `kind` key")
            }
        }

        fun assertPlotSpec(opts: Map) {
            val identified = isPlotSpec(opts) || isGGBunchSpec(opts)
            if (!identified) {
                throw IllegalArgumentException("Invalid root feature kind: absent or unsupported  `kind` key")
            }
        }

        fun isFailure(opts: Map): Boolean {
            return opts.containsKey(ERROR_MESSAGE)
        }

        fun getErrorMessage(opts: Map): String {
            return opts[ERROR_MESSAGE].toString()
        }

        fun isPlotSpec(opts: Map<*, *>): Boolean {
            return Kind.PLOT == specKind(opts)
        }

        fun isGGBunchSpec(opts: Map<*, *>): Boolean {
            return Kind.GG_BUNCH == specKind(opts)
        }

        fun specKind(opts: Map<*, *>): Any? {
            return opts[Meta.KIND]
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy