Skip to content

Commit f1663ab

Browse files
committed
Add geomBlank() and ExpandLimits()
1 parent 17acc41 commit f1663ab

File tree

5 files changed

+285
-4
lines changed

5 files changed

+285
-4
lines changed
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/*
2+
* Copyright (c) 2022. JetBrains s.r.o.
3+
* Use of this source code is governed by the MIT license that can be found in the LICENSE file.
4+
*/
5+
6+
package frontendContextDemo.scripts
7+
8+
import demoData.AutoMpg
9+
import frontendContextDemo.ScriptInBatikContext
10+
import org.jetbrains.letsPlot.geom.geomPoint
11+
import org.jetbrains.letsPlot.ggplot
12+
import org.jetbrains.letsPlot.label.ggtitle
13+
import org.jetbrains.letsPlot.scale.expandLimits
14+
15+
object ExpandLimits {
16+
@JvmStatic
17+
fun main(args: Array<String>) {
18+
ScriptInBatikContext.eval("expandLimits()") {
19+
20+
val mpgData = AutoMpg.map()
21+
22+
run {
23+
val p = ggplot(mpgData) {
24+
x = "miles per gallon"
25+
y = "vehicle weight (lbs.)"
26+
}
27+
28+
29+
// Default
30+
(p + geomPoint() + ggtitle("Default")).show()
31+
32+
// Expand Lower Limit Along the x-axis
33+
(p + geomPoint() + expandLimits(x = 0) + ggtitle("expandLimits(x = 0)")).show()
34+
35+
// Expand Limits Along the y-axis
36+
(p + geomPoint() + expandLimits(
37+
y = listOf(
38+
1000,
39+
9000
40+
)
41+
) + ggtitle("expandLimits(y = listOf(1000, 9000))")).show()
42+
43+
// Expand Lower Limits Along Both Axes
44+
(p + geomPoint() + expandLimits(x = 0, y = 0) + ggtitle("expandLimits(x = 0, y = 0)")).show()
45+
}
46+
47+
run {
48+
// Expanding Color-scale Limits
49+
val p = ggplot(mpgData) {
50+
x = "miles per gallon"
51+
y = "vehicle weight (lbs.)"
52+
color = "number of cylinders"
53+
}
54+
55+
56+
// Default
57+
(p + geomPoint() + ggtitle("Default with color")).show()
58+
59+
// Expand the color-scale limits.
60+
(p + geomPoint() + expandLimits(color = 2..10) + ggtitle("expandLimits(color = 2..10)")).show()
61+
}
62+
}
63+
}
64+
}

plot-api/src/commonMain/kotlin/org/jetbrains/letsPlot/Geom.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,9 @@ import org.jetbrains.letsPlot.intern.layer.geom.*
2424
* ```
2525
*/
2626
object Geom {
27-
// val blank = GeomOptions(
28-
// GeomKind.BLANK
29-
// )
27+
internal val blank = GeomOptions(
28+
GeomKind.BLANK
29+
)
3030

3131
@Suppress("ClassName")
3232
class point(
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
/*
2+
* Copyright (c) 2021. JetBrains s.r.o.
3+
* Use of this source code is governed by the MIT license that can be found in the LICENSE file.
4+
*/
5+
6+
package org.jetbrains.letsPlot.geom
7+
8+
import org.jetbrains.letsPlot.Geom.blank
9+
import org.jetbrains.letsPlot.Geom.point
10+
import org.jetbrains.letsPlot.Stat
11+
import org.jetbrains.letsPlot.intern.Layer
12+
import org.jetbrains.letsPlot.intern.layer.*
13+
import org.jetbrains.letsPlot.intern.layer.geom.PointAesthetics
14+
import org.jetbrains.letsPlot.intern.layer.geom.PointMapping
15+
import org.jetbrains.letsPlot.pos.positionIdentity
16+
import org.jetbrains.letsPlot.spatial.SpatialDataset
17+
import org.jetbrains.letsPlot.tooltips.TooltipOptions
18+
19+
@Suppress("ClassName")
20+
/**
21+
* Draws nothing, but can be a useful way of ensuring common scales between different plots (see `expandLimits()`).
22+
* Also, can help to avoid the "No layers in plot" error when building plots using automated tools.
23+
*
24+
* @param data The data to be displayed in this layer. If null, the default, the data
25+
* is inherited from the plot data as specified in the call to [letsPlot][org.jetbrains.letsPlot.letsPlot].
26+
* @param stat The statistical transformation to use on the data for this layer.
27+
* Supported transformations: `Stat.identity`, `Stat.bin()`, `Stat.count()`, etc. see [Stat][org.jetbrains.letsPlot.Stat].
28+
* @param position Position adjustment: `positionIdentity`, `positionStack()`, `positionDodge()`, etc. see
29+
* [Position](https://lets-plot.org/kotlin/-lets--plot--kotlin/org.jetbrains.letsPlot.pos/).
30+
* @param showLegend default = true.
31+
* false - do not show legend for this layer.
32+
* @param inheritAes default = false.
33+
* false - do not combine the layer aesthetic mappings with the plot shared mappings.
34+
* @param manualKey String or result of the call to the `layerKey()` function.
35+
* The key to show in the manual legend. Specifies the text for the legend label or advanced settings using the `layerKey()` function.
36+
* @param sampling Result of the call to the `samplingXxx()` function.
37+
* To prevent any sampling for this layer pass value `samplingNone`.
38+
* For more info see [sampling.html](https://lets-plot.org/kotlin/sampling.html).
39+
* @param tooltips Result of the call to the `layerTooltips()` function.
40+
* Specifies appearance, style and content.
41+
* @param orientation Specifies the axis that the layer's stat and geom should run along, default = "x".
42+
* Possible values: "x", "y".
43+
* @param map Data-structure containing series of planar shapes and, optionally, associates data series (for example:
44+
* names of States and their boundaries).
45+
*
46+
* Supported shapes: Point and MultiPoint.
47+
* All coordinates should be encoded as decimal degrees in WGS84 coordinate reference system.
48+
*
49+
* Can be used with parameter `mapJoin` for joining data and map coordinates.
50+
* @param mapJoin Pair of Names or Pair of Lists of Names.
51+
* Specifies column names to join the `data` and the `map` coordinates on.
52+
* - Pair.first: column name or list of column names in the `data` dataframe.
53+
* - Pair.second: column name or list of column names in the `map` dataframe.
54+
* @param useCRS By default, all coordinates are converted into degrees of longitude and latitude,
55+
* and these map coordinates are projected onto the screen coordinates using Mercator projection.
56+
* Specify useCRS = "provided" to keep the SpatialDataset's original coordinate reference system (CRS).
57+
* @param x X-axis value.
58+
* @param y Y-axis value.
59+
* @param alpha Transparency level of a layer. Understands numbers between 0 and 1.
60+
* @param color Color of the geometry.
61+
* For more info see: [aesthetics.html#color-and-fill](https://lets-plot.org/kotlin/aesthetics.html#color-and-fill).
62+
* @param fill Color to paint shape's inner points.
63+
* Is applied only to the points of shapes having inner points.
64+
* For more info see: [aesthetics.html#color-and-fill](https://lets-plot.org/kotlin/aesthetics.html#color-and-fill).
65+
* @param shape Shape of the point.
66+
* For more info see: [aesthetics.html#point-shapes](https://lets-plot.org/kotlin/aesthetics.html#point-shapes).
67+
* @param size Size of the point.
68+
* @param stroke Width of the shape border. Applied only to the shapes having border.
69+
* @param angle Rotation angle of the shape, in degrees.
70+
* @param sizeUnit Relates the size of the point to the length of the unit step along one of the axes.
71+
* Possible values: "x", "y". If not specified, no fitting is performed.
72+
* @param colorBy default = "color" ("fill", "color", "paint_a", "paint_b", "paint_c").
73+
* Defines the color aesthetic for the geometry.
74+
* @param fillBy default = "fill" ("fill", "color", "paint_a", "paint_b", "paint_c").
75+
* Defines the fill aesthetic for the geometry.
76+
* @param mapping Set of aesthetic mappings.
77+
* Aesthetic mappings describe the way that variables in the data are
78+
* mapped to plot "aesthetics".
79+
*/
80+
class geomBlank(
81+
data: Map<*, *>? = null,
82+
stat: StatOptions = Stat.identity,
83+
position: PosOptions = positionIdentity,
84+
showLegend: Boolean = true,
85+
inheritAes: Boolean? = null,
86+
manualKey: Any? = null,
87+
sampling: SamplingOptions? = null,
88+
tooltips: TooltipOptions? = null,
89+
orientation: String? = null,
90+
override val map: SpatialDataset? = null,
91+
override val mapJoin: Pair<Any, Any>? = null,
92+
override val useCRS: String? = null,
93+
override val x: Number? = null,
94+
override val y: Number? = null,
95+
override val alpha: Number? = null,
96+
override val color: Any? = null,
97+
override val fill: Any? = null,
98+
override val shape: Any? = null,
99+
override val size: Number? = null,
100+
override val stroke: Number? = null,
101+
override val angle: Number? = null,
102+
override val sizeUnit: String? = null,
103+
override val colorBy: String? = null,
104+
override val fillBy: String? = null,
105+
mapping: PointMapping.() -> Unit = {}
106+
107+
) : PointAesthetics,
108+
WithSizeUnitOption,
109+
WithSpatialParameters,
110+
WithColorOption,
111+
WithFillOption,
112+
Layer(
113+
mapping = PointMapping().apply(mapping).seal(),
114+
data = data,
115+
geom = blank,
116+
stat = stat,
117+
position = position,
118+
showLegend = showLegend,
119+
inheritAes = inheritAes ?: false,
120+
manualKey = manualKey,
121+
sampling = sampling,
122+
tooltips = tooltips,
123+
orientation = orientation
124+
) {
125+
override fun seal() = super<PointAesthetics>.seal() +
126+
super<WithSizeUnitOption>.seal() +
127+
super<WithColorOption>.seal() +
128+
super<WithFillOption>.seal()
129+
}
130+
131+

plot-api/src/commonMain/kotlin/org/jetbrains/letsPlot/intern/GeomKind.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
package org.jetbrains.letsPlot.intern
77

88
enum class GeomKind {
9-
// BLANK, not implemented
9+
BLANK,
1010
PATH,
1111
LINE,
1212
SMOOTH,
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
/*
2+
* Copyright (c) 2021. JetBrains s.r.o.
3+
* Use of this source code is governed by the MIT license that can be found in the LICENSE file.
4+
*/
5+
6+
package org.jetbrains.letsPlot.scale
7+
8+
import org.jetbrains.letsPlot.core.commons.data.SeriesUtil.isFinite
9+
import org.jetbrains.letsPlot.geom.geomBlank
10+
import org.jetbrains.letsPlot.intern.Feature
11+
12+
/**
13+
* Expands the plot limits to include additional data values.
14+
*
15+
* This function extends the plot boundaries to encompass new data points,
16+
* whether a single value or multiple values are provided. It acts as a
17+
* thin wrapper around geomBlank().
18+
*
19+
* @param x Value (or values) that should be included in x-scale.
20+
* @param y Value (or values) that should be included in y-scale.
21+
* @param size
22+
* @param color
23+
* @param fill
24+
* @param alpha
25+
* @param shape
26+
*
27+
* @return Layer object. A result of the `geomBlank()` call.
28+
*
29+
* ## Notes
30+
*
31+
* The data value (or values) could be a single value or an iterable or a Pair or Triple.
32+
*
33+
* ## Examples
34+
*
35+
* ToDo
36+
*
37+
*/
38+
fun expandLimits(
39+
x: Any? = null,
40+
y: Any? = null,
41+
size: Any? = null,
42+
color: Any? = null,
43+
fill: Any? = null,
44+
alpha: Any? = null,
45+
shape: Any? = null
46+
): Feature {
47+
48+
val standardized: Map<String, List<Any?>> = mapOf(
49+
"x" to standardize(x),
50+
"y" to standardize(y),
51+
"size" to standardize(size),
52+
"color" to standardize(color),
53+
"fill" to standardize(fill),
54+
"alpha" to standardize(alpha),
55+
"shape" to standardize(shape),
56+
)
57+
58+
// Drop all undefined but keep x and y even if undefined.
59+
val cleaned = standardized.filter { (k, v) ->
60+
k == "x" || k == "y" || v.any { it != null && if (it is Number) isFinite(it.toDouble()) else true }
61+
}
62+
63+
val maxLen = cleaned.values.maxBy { it.size }.size
64+
val data = cleaned.mapValues { (_, v) ->
65+
v + List(maxLen - v.size) { null }
66+
}
67+
68+
return geomBlank {
69+
this.x = data.getValue("x")
70+
this.y = data.getValue("y")
71+
data["size"]?.let { this.size = it }
72+
data["color"]?.let { this.color = it }
73+
data["fill"]?.let { this.fill = it }
74+
data["alpha"]?.let { this.alpha = it }
75+
data["shape"]?.let { this.shape = it }
76+
}
77+
}
78+
79+
private fun standardize(v: Any?): List<Any?> {
80+
return when (v) {
81+
is Iterable<*> -> v.toList()
82+
is Pair<*, *> -> listOf(v.first, v.second)
83+
is Triple<*, *, *> -> listOf(v.first, v.second, v.third)
84+
else -> listOf(v)
85+
}
86+
}

0 commit comments

Comments
 (0)