-
Notifications
You must be signed in to change notification settings - Fork 47
/
GeomProto.kt
353 lines (311 loc) · 12.9 KB
/
GeomProto.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
/*
* Copyright (c) 2023. JetBrains s.r.o.
* Use of this source code is governed by the MIT license that can be found in the LICENSE file.
*/
package org.jetbrains.letsPlot.core.spec
import org.jetbrains.letsPlot.commons.intern.spatial.projections.identity
import org.jetbrains.letsPlot.commons.intern.spatial.projections.mercator
import org.jetbrains.letsPlot.core.plot.base.GeomKind
import org.jetbrains.letsPlot.core.plot.base.GeomKind.*
import org.jetbrains.letsPlot.core.plot.builder.assemble.geom.DefaultSampling
import org.jetbrains.letsPlot.core.plot.builder.assemble.geom.GeomProvider
import org.jetbrains.letsPlot.core.plot.builder.coord.CoordProvider
import org.jetbrains.letsPlot.core.plot.builder.coord.CoordProviders
import org.jetbrains.letsPlot.core.plot.builder.sampling.Sampling
import org.jetbrains.letsPlot.core.plot.builder.sampling.Samplings
import org.jetbrains.letsPlot.core.spec.Option.Geom
import org.jetbrains.letsPlot.core.spec.Option.Layer
import org.jetbrains.letsPlot.core.spec.Option.Meta
import org.jetbrains.letsPlot.core.spec.Option.Pos
import org.jetbrains.letsPlot.core.spec.config.OptionsAccessor
import org.jetbrains.letsPlot.core.spec.conversion.AesOptionConversion
class GeomProto(val geomKind: GeomKind) {
fun geomProvider(layerConfig: OptionsAccessor, aopConversion: AesOptionConversion): GeomProvider {
return GeomProviderFactory.createGeomProvider(geomKind, layerConfig, aopConversion)
}
fun defaultOptions(): Map<String, Any> {
require(DEFAULTS.containsKey(geomKind)) { "Default values doesn't support geom kind: '$geomKind'" }
return DEFAULTS.getValue(geomKind)
}
fun preferredCoordinateSystem(layerConfig: OptionsAccessor): CoordProvider? {
return when (geomKind) {
TILE,
BIN_2D,
CONTOUR,
CONTOURF,
DENSITY2D,
DENSITY2DF,
RASTER,
IMAGE -> CoordProviders.fixed(1.0)
MAP -> CoordProviders.map(projection = identity().takeIf { layerConfig.has(Layer.USE_CRS) } ?: mercator())
else -> null
}
}
fun preferredSampling(): Sampling {
return when (geomKind) {
POINT -> DefaultSampling.POINT
PATH -> DefaultSampling.PATH
LINE -> DefaultSampling.LINE
SMOOTH -> DefaultSampling.SMOOTH
BAR -> DefaultSampling.BAR
HISTOGRAM -> DefaultSampling.HISTOGRAM
DOT_PLOT -> DefaultSampling.DOT_PLOT
TILE -> DefaultSampling.TILE
BIN_2D -> DefaultSampling.BIN_2D
ERROR_BAR -> DefaultSampling.ERROR_BAR
CROSS_BAR -> DefaultSampling.CROSS_BAR
LINE_RANGE -> DefaultSampling.LINE_RANGE
POINT_RANGE -> DefaultSampling.POINT_RANGE
CONTOUR -> DefaultSampling.CONTOUR
CONTOURF -> DefaultSampling.CONTOURF
POLYGON -> DefaultSampling.POLYGON
MAP -> DefaultSampling.MAP
AB_LINE -> DefaultSampling.AB_LINE
H_LINE -> DefaultSampling.H_LINE
V_LINE -> DefaultSampling.V_LINE
BOX_PLOT -> Samplings.NONE // DefaultSampling.BOX_PLOT
AREA_RIDGES -> DefaultSampling.AREA_RIDGES
VIOLIN -> DefaultSampling.VIOLIN
Y_DOT_PLOT -> DefaultSampling.Y_DOT_PLOT
RIBBON -> DefaultSampling.RIBBON
AREA -> DefaultSampling.AREA
DENSITY -> DefaultSampling.DENSITY
DENSITY2D -> DefaultSampling.DENSITY2D
DENSITY2DF -> DefaultSampling.DENSITY2DF
JITTER -> DefaultSampling.JITTER
Q_Q -> DefaultSampling.Q_Q
Q_Q_2 -> DefaultSampling.Q_Q
Q_Q_LINE -> DefaultSampling.Q_Q_LINE
Q_Q_2_LINE -> DefaultSampling.Q_Q_LINE
FREQPOLY -> DefaultSampling.FREQPOLY
STEP -> DefaultSampling.STEP
RECT -> DefaultSampling.RECT
SEGMENT -> DefaultSampling.SEGMENT
TEXT, LABEL -> DefaultSampling.TEXT
PIE -> DefaultSampling.PIE
LOLLIPOP -> DefaultSampling.LOLLIPOP
LIVE_MAP,
RASTER,
IMAGE -> Samplings.NONE
}
}
fun hasOwnPositionAdjustmentOptions(layerOptions: OptionsAccessor): Boolean {
return when (geomKind) {
JITTER -> layerOptions.hasOwn(Geom.Jitter.WIDTH) || layerOptions.hasOwn(Geom.Jitter.HEIGHT)
TEXT, LABEL -> layerOptions.hasOwn(Geom.Text.NUDGE_X) || layerOptions.hasOwn(Geom.Text.NUDGE_Y)
else -> false
}
}
fun preferredPositionAdjustmentOptions(layerOptions: OptionsAccessor): Map<String, Any> {
val posOptionValue: Any = when (geomKind) {
JITTER -> mapOf(
Meta.NAME to PosProto.JITTER,
Pos.Jitter.WIDTH to layerOptions.getDouble(Geom.Jitter.WIDTH),
Pos.Jitter.HEIGHT to layerOptions.getDouble(Geom.Jitter.HEIGHT),
Pos.Jitter.SEED to layerOptions.getLong(Geom.Jitter.SEED)
)
Y_DOT_PLOT -> if (layerOptions.hasOwn(Geom.YDotplot.STACKGROUPS) &&
layerOptions.getBoolean(Geom.YDotplot.STACKGROUPS)
) {
PosProto.IDENTITY
} else {
mapOf(
Meta.NAME to PosProto.DODGE,
Pos.Dodge.WIDTH to 0.95
)
}
TEXT, LABEL -> if (layerOptions.hasOwn(Geom.Text.NUDGE_X) || layerOptions.hasOwn(Geom.Text.NUDGE_Y)) {
mapOf(
Meta.NAME to PosProto.NUDGE,
Pos.Nudge.WIDTH to layerOptions.getDouble(Geom.Text.NUDGE_X),
Pos.Nudge.HEIGHT to layerOptions.getDouble(Geom.Text.NUDGE_Y)
)
} else {
PosProto.IDENTITY
}
else -> {
// Layer also can be set via stat_xxx function
when (StatKind.safeValueOf(layerOptions.getStringSafe(Layer.STAT))) {
// Added for synchronization with default position for boxplot
StatKind.BOXPLOT_OUTLIER -> mapOf(
Meta.NAME to PosProto.DODGE,
Pos.Dodge.WIDTH to 0.95
)
// Some other geoms have stateless position adjustments defined in `defaults`
// Otherwise it's just `identity`
else -> DEFAULTS[geomKind]?.get(Layer.POS) ?: PosProto.IDENTITY
}
}
}
return if (posOptionValue is Map<*, *>) {
@Suppress("UNCHECKED_CAST")
(posOptionValue.filterValues { it != null } as Map<String, Any>)
} else {
mapOf(
Meta.NAME to posOptionValue as String
)
}
}
private companion object {
private val DEFAULTS = HashMap<GeomKind, Map<String, Any>>()
private val COMMON = commonDefaults()
init {
for (geomKind in values()) {
DEFAULTS[geomKind] = COMMON
}
DEFAULTS[SMOOTH] = smoothDefaults()
DEFAULTS[BAR] = barDefaults()
DEFAULTS[HISTOGRAM] = histogramDefaults()
DEFAULTS[DOT_PLOT] = dotplotDefaults()
DEFAULTS[CONTOUR] = contourDefaults()
DEFAULTS[CONTOURF] = contourfDefaults()
DEFAULTS[CROSS_BAR] = crossBarDefaults()
DEFAULTS[BOX_PLOT] = boxplotDefaults()
DEFAULTS[AREA_RIDGES] = areaRidgesDefaults()
DEFAULTS[VIOLIN] = violinDefaults()
DEFAULTS[Y_DOT_PLOT] = yDotplotDefaults()
DEFAULTS[AREA] = areaDefaults()
DEFAULTS[DENSITY] = densityDefaults()
DEFAULTS[DENSITY2D] = density2dDefaults()
DEFAULTS[DENSITY2DF] = density2dfDefaults()
DEFAULTS[Q_Q] = qqDefaults()
DEFAULTS[Q_Q_2] = qq2Defaults()
DEFAULTS[Q_Q_LINE] = qqLineDefaults()
DEFAULTS[Q_Q_2_LINE] = qq2LineDefaults()
DEFAULTS[FREQPOLY] = freqpolyDefaults()
DEFAULTS[BIN_2D] = bin2dDefaults()
DEFAULTS[PIE] = pieDefaults()
}
private fun commonDefaults(): Map<String, Any> {
val defaults = HashMap<String, Any>()
defaults[Layer.STAT] = "identity"
return defaults
}
private fun smoothDefaults(): Map<String, Any> {
val defaults = HashMap<String, Any>()
defaults[Layer.STAT] = "smooth"
return defaults
}
private fun barDefaults(): Map<String, Any> {
val defaults = HashMap<String, Any>()
defaults[Layer.STAT] = "count"
defaults[Layer.POS] = PosProto.STACK
return defaults
}
private fun histogramDefaults(): Map<String, Any> {
val defaults = HashMap<String, Any>()
defaults[Layer.STAT] = "bin"
defaults[Layer.POS] = PosProto.STACK
return defaults
}
private fun dotplotDefaults(): Map<String, Any> {
val defaults = HashMap<String, Any>()
defaults[Layer.STAT] = "dotplot"
return defaults
}
private fun contourDefaults(): Map<String, Any> {
val defaults = HashMap<String, Any>()
defaults[Layer.STAT] = "contour"
return defaults
}
private fun contourfDefaults(): Map<String, Any> {
val defaults = HashMap<String, Any>()
defaults[Layer.STAT] = "contourf"
return defaults
}
private fun crossBarDefaults(): Map<String, Any> {
val defaults = HashMap<String, Any>()
defaults[Layer.STAT] = "identity"
defaults[Layer.POS] = mapOf(
Meta.NAME to PosProto.DODGE,
Pos.Dodge.WIDTH to 0.95
)
return defaults
}
private fun boxplotDefaults(): Map<String, Any> {
val defaults = HashMap<String, Any>()
defaults[Layer.STAT] = "boxplot"
defaults[Layer.POS] = mapOf(
Meta.NAME to PosProto.DODGE,
Pos.Dodge.WIDTH to 0.95
)
return defaults
}
private fun areaRidgesDefaults(): Map<String, Any> {
val defaults = HashMap<String, Any>()
defaults[Layer.STAT] = "densityridges"
return defaults
}
private fun violinDefaults(): Map<String, Any> {
val defaults = HashMap<String, Any>()
defaults[Layer.STAT] = "ydensity"
defaults[Layer.POS] = mapOf(
Meta.NAME to PosProto.DODGE,
Pos.Dodge.WIDTH to 0.95
)
return defaults
}
private fun yDotplotDefaults(): Map<String, Any> {
val defaults = HashMap<String, Any>()
defaults[Layer.STAT] = "ydotplot"
return defaults
}
private fun areaDefaults(): Map<String, Any> {
val defaults = HashMap<String, Any>()
defaults[Layer.STAT] = "identity"
defaults[Layer.POS] = PosProto.STACK
return defaults
}
private fun densityDefaults(): Map<String, Any> {
val defaults = HashMap<String, Any>()
defaults[Layer.STAT] = "density"
return defaults
}
private fun density2dDefaults(): Map<String, Any> {
val defaults = HashMap<String, Any>()
defaults[Layer.STAT] = "density2d"
return defaults
}
private fun density2dfDefaults(): Map<String, Any> {
val defaults = HashMap<String, Any>()
defaults[Layer.STAT] = "density2df"
return defaults
}
private fun qqDefaults(): Map<String, Any> {
val defaults = HashMap<String, Any>()
defaults[Layer.STAT] = "qq"
return defaults
}
private fun qq2Defaults(): Map<String, Any> {
val defaults = HashMap<String, Any>()
defaults[Layer.STAT] = "qq2"
return defaults
}
private fun qqLineDefaults(): Map<String, Any> {
val defaults = HashMap<String, Any>()
defaults[Layer.STAT] = "qq_line"
return defaults
}
private fun qq2LineDefaults(): Map<String, Any> {
val defaults = HashMap<String, Any>()
defaults[Layer.STAT] = "qq2_line"
return defaults
}
private fun freqpolyDefaults(): Map<String, Any> {
val defaults = HashMap<String, Any>()
defaults[Layer.STAT] = "bin"
return defaults
}
private fun bin2dDefaults(): Map<String, Any> {
return mapOf(
Layer.STAT to "bin2d"
)
}
private fun pieDefaults(): Map<String, Any> {
return mapOf(
Layer.STAT to "count2d",
Geom.Pie.SPACER_COLOR to "paper"
)
}
}
}