in plot-builder/src/commonMain/kotlin/org/jetbrains/letsPlot/core/plot/builder/assemble/PlotAssemblerUtil.kt [21:157]
fun createLegends(
ctx: PlotContext,
geomTiles: PlotGeomTiles,
scaleMappersNP: Map<Aes<*>, ScaleMapper<*>>,
guideOptionsMap: Map<GuideKey, GuideOptionsList>,
legendTheme: LegendTheme,
panelTheme: PanelTheme
): List<LegendBoxInfo> {
val legendAssemblerByTitle = LinkedHashMap<String, LegendAssembler>()
val colorBarAssemblerByTitle = LinkedHashMap<String, ColorBarAssembler>()
for ((layerIndex, layerInfo) in geomTiles.layerInfos().withIndex()) {
val layerConstantByAes = HashMap<Aes<*>, Any>()
for (aes in layerInfo.renderedAes()) {
if (layerInfo.hasConstant(aes)) {
layerConstantByAes[aes] = layerInfo.getConstant(aes)!!
}
}
val aesListByScaleName = LinkedHashMap<String, MutableList<Aes<*>>>()
val aesList = mappedRenderedAesToCreateGuides(layerInfo, guideOptionsMap)
for (aes in aesList) {
val scale = ctx.getScale(aes)
val colorBarOptions: ColorBarOptions? = guideOptionsMap[GuideKey.fromAes(aes)]
?.getColorBarOptions()
?.also { checkFitsColorBar(aes, scale) }
if (colorBarOptions != null || fitsColorBar(aes, scale)) {
// Colorbar
@Suppress("UNCHECKED_CAST")
val colorBarAssembler = createColorBarAssembler(
scale.name,
ctx.overallTransformedDomain(aes),
scale,
scaleMappersNP.getValue(aes) as ScaleMapper<Color>,
colorBarOptions,
legendTheme
)
val colorbarName = colorBarAssemblerByTitle[scale.name]?.let { existingAssembler ->
if (colorBarAssembler.equalScalesAndOptions(existingAssembler)) {
scale.name
} else {
// Don't just replace an existing colorbar (see LP-760: ggmarginal(): broken coloring)
// Add under another key
"${scale.name} (${aes.name})"
}
} ?: scale.name
colorBarAssemblerByTitle[colorbarName] = colorBarAssembler.withTitle(colorbarName)
} else {
// Legend
aesListByScaleName.getOrPut(scale.name) { ArrayList() }.add(aes)
}
}
for ((scaleName, aesListForScaleName) in aesListByScaleName) {
val legendAssembler = legendAssemblerByTitle.getOrPut(scaleName) {
LegendAssembler(
scaleName,
guideOptionsMap,
scaleMappersNP,
legendTheme,
panelTheme
)
}
val guideKeysForScaleName = aesListForScaleName.map(GuideKey.Companion::fromAes)
val allOverrideAesValues = guideOptionsMap
.filterKeys { it in guideKeysForScaleName }
.values
.mapNotNull { it.getLegendOptions()?.overrideAesValues }
.flatMap { it.entries }
.associate { it.key to it.value }
legendAssembler.addLayer(
keyFactory = layerInfo.legendKeyElementFactory,
aesList = aesListForScaleName,
overrideAesValues = allOverrideAesValues,
constantByAes = layerConstantByAes,
aestheticsDefaults = layerInfo.aestheticsDefaults,
colorByAes = layerInfo.colorByAes,
fillByAes = layerInfo.fillByAes,
isMarginal = layerInfo.isMarginal,
ctx = ctx
)
}
// custom legend
layerInfo.customLegendOptions?.let { legendOptions ->
val guideKey = GuideKey.fromName(legendOptions.group)
if (guideOptionsMap[guideKey]?.hasNone() == true) return@let
val legendTitle = guideOptionsMap[guideKey]?.getTitle() ?: legendOptions.group
val customLegendAssembler = legendAssemblerByTitle.getOrPut(legendTitle) {
LegendAssembler(
legendTitle,
guideOptionsMap,
scaleMappersNP,
legendTheme,
panelTheme
)
}
val allOverrideAesValues = processOverrideAesValues(
guideOptionsMap[guideKey]?.getLegendOptions()?.overrideAesValues,
legendOptions.index ?: layerIndex
)
customLegendAssembler.addCustomLayer(
customLegendOptions = legendOptions,
keyFactory = layerInfo.legendKeyElementFactory,
overrideAesValues = allOverrideAesValues,
constantByAes = layerConstantByAes,
aestheticsDefaults = layerInfo.aestheticsDefaults,
colorByAes = layerInfo.colorByAes,
fillByAes = layerInfo.fillByAes,
isMarginal = layerInfo.isMarginal
)
}
}
val legendBoxInfos = ArrayList<LegendBoxInfo>()
for (legendTitle in colorBarAssemblerByTitle.keys) {
val boxInfo = colorBarAssemblerByTitle.getValue(legendTitle).createColorBar()
boxInfo?.let { legendBoxInfos.add(it) }
}
for (legendTitle in legendAssemblerByTitle.keys) {
val boxInfo = legendAssemblerByTitle.getValue(legendTitle).createLegend()
boxInfo?.let { legendBoxInfos.add(it) }
}
return legendBoxInfos
}