Skip to content

Commit

Permalink
Fix #857 - theme/flavor plot background is not shown on plot with geo…
Browse files Browse the repository at this point in the history
…m_livemap() layer
  • Loading branch information
IKupriyanov-HORIS committed Aug 28, 2023
1 parent 5f5f035 commit 7d3fddb
Show file tree
Hide file tree
Showing 9 changed files with 273 additions and 135 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ class SvgPathElement() : SvgGraphicsElement(), SvgTransformable,
SvgShape {

companion object {
val D: SvgAttributeSpec<SvgPathData> =
SvgAttributeSpec.createSpec("d")
val FILL_RULE: SvgAttributeSpec<FillRule> = SvgAttributeSpec.createSpec("fill-rule")
val D: SvgAttributeSpec<SvgPathData> = SvgAttributeSpec.createSpec("d")
}

override val elementName = "path"
Expand All @@ -34,6 +34,10 @@ class SvgPathElement() : SvgGraphicsElement(), SvgTransformable,
setAttribute(D, d)
}

fun fillRule(): Property<FillRule?> {
return getAttribute(FILL_RULE)
}

fun d(): Property<SvgPathData?> {
return getAttribute(D)
}
Expand Down Expand Up @@ -77,4 +81,13 @@ class SvgPathElement() : SvgGraphicsElement(), SvgTransformable,
override fun pointToAbsoluteCoordinates(point: DoubleVector): DoubleVector {
return container().getPeer()!!.applyTransform(this, point)
}

enum class FillRule(private val myAttrString: String) {
EVEN_ODD("evenodd"),
NON_ZERO("nonzero");

override fun toString(): String {
return myAttrString
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/*
* 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 demoAndTestShared

import org.jetbrains.letsPlot.core.spec.*

object LiveMapUtil {
object Tiles {
val production = mapOf(
"kind" to "vector_lets_plot",
"url" to "wss://tiles.datalore.jetbrains.com/",
"theme" to "color",
"attribution" to "Map: <a href=\"https://github.com/JetBrains/lets-plot\">\u00a9 Lets-Plot</a>, map data: <a href=\"https://www.openstreetmap.org/copyright\">\u00a9 OpenStreetMap contributors</a>."
)

val productionDark = mapOf(
"kind" to "vector_lets_plot",
"url" to "wss://tiles.datalore.jetbrains.com/",
"theme" to "dark",
"attribution" to "Map: <a href=\"https://github.com/JetBrains/lets-plot\">\u00a9 Lets-Plot</a>, map data: <a href=\"https://www.openstreetmap.org/copyright\">\u00a9 OpenStreetMap contributors</a>."
)

val dev = mapOf(
"tiles" to mapOf(
"kind" to "vector_lets_plot",
"url" to "ws://10.0.0.127:3943",
"min_zoom" to 1,
"max_zoom" to 15,
"theme" to "color"
)
)

val nasa = mapOf(
"tiles" to mapOf(
"kind" to "raster_zxy",
"url" to "https://gibs.earthdata.nasa.gov/wmts/epsg3857/best/ASTER_GDEM_Greyscale_Shaded_Relief/default//GoogleMapsCompatible_Level12/{z}/{y}/{x}.jpg",
"attribution" to "<a href=\"https://earthdata.nasa.gov/eosdis/science-system-description/eosdis-components/gibs\">\u00a9 NASA Global Imagery Browse Services (GIBS)</a>",
"min_zoom" to 1,
"max_zoom" to 12
)
)

val osm = mapOf(
"tiles" to mapOf(
"kind" to "raster_zxy",
"url" to "https://[abc].tile.openstreetmap.org/{z}/{x}/{y}.png",
"attribution" to "<a href=\"https://www.openstreetmap.org/copyright\">© OpenStreetMap contributors</a>"
)
)

val debug = mapOf(
"tiles" to mapOf(
"kind" to "chessboard"
)
)
}

fun MutableMap<String, Any>.updateTiles(tilesSpec: Map<String, Any> = Tiles.production, force: Boolean = false) = apply {
fun update(plotSpec: Map<*, *>) {
try {
val liveMapSpec = plotSpec.getList("layers")!!.asMaps().first()
if (force || Option.Geom.LiveMap.TILES !in liveMapSpec) {
liveMapSpec.asMutable()[Option.Geom.LiveMap.TILES] = tilesSpec
}

} catch (e: Exception) {
println(e)
}
}

when(val specKind = get(Option.Meta.KIND)) {
Option.Meta.Kind.PLOT -> update(this)
Option.Meta.Kind.SUBPLOTS -> getMaps(Option.SubPlots.FIGURES)!!.forEach(::update)
Option.Meta.Kind.GG_BUNCH -> getMaps(Option.GGBunch.ITEMS)!!.flatMap { it.getMaps(Option.GGBunch.Item.FEATURE_SPEC)!! }.forEach(::update)
else -> error("Unknown spec kind: $specKind")
}
}
}
122 changes: 74 additions & 48 deletions demo/livemap/src/commonMain/kotlin/demo/livemap/plotDemo/LiveMap.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,16 @@

package demo.livemap.plotDemo

import org.jetbrains.letsPlot.core.spec.asMaps
import org.jetbrains.letsPlot.core.spec.asMutable
import org.jetbrains.letsPlot.core.spec.getList
import demoAndTestShared.LiveMapUtil.Tiles
import demoAndTestShared.LiveMapUtil.updateTiles
import demoAndTestShared.parsePlotSpec
import kotlin.random.Random

@Suppress("unused")
class LiveMap {
fun plotSpecList(): List<MutableMap<String, Any>> {
return listOf(
darcula().updateTiles(Tiles.productionDark),
variadicPath(),
titanic(),
airports(),
Expand All @@ -30,52 +30,84 @@ class LiveMap {
osmTiles(),
bunch(),
facet()
)
).onEach {
it.updateTiles()
}
}

object Tileset {
val nasa = mapOf(
"tiles" to mapOf(
"kind" to "raster_zxy",
"url" to "https://gibs.earthdata.nasa.gov/wmts/epsg3857/best/ASTER_GDEM_Greyscale_Shaded_Relief/default//GoogleMapsCompatible_Level12/{z}/{y}/{x}.jpg",
"attribution" to "<a href=\"https://earthdata.nasa.gov/eosdis/science-system-description/eosdis-components/gibs\">\u00a9 NASA Global Imagery Browse Services (GIBS)</a>",
"min_zoom" to 1,
"max_zoom" to 12
)
)

val osm = mapOf(
"tiles" to mapOf(
"kind" to "raster_zxy",
"url" to "https://[abc].tile.openstreetmap.org/{z}/{x}/{y}.png",
"attribution" to "<a href=\"https://www.openstreetmap.org/copyright\">© OpenStreetMap contributors</a>"
)
)

val devVector = mapOf(
"tiles" to mapOf(
"kind" to "vector_lets_plot",
"url" to "ws://10.0.0.127:3943",
"min_zoom" to 1,
"max_zoom" to 15,
"theme" to "color"
)
)

val chessboard = mapOf(
"tiles" to mapOf(
"kind" to "chessboard"
)
)
}
private fun darcula(): MutableMap<String, Any> {
val spec = """
|{
| "kind": "plot",
| "data": {
| "x": [ 20.0, 30.0, 40.0 ],
| "y": [ 60.0, 60.0, 60.0 ],
| "s": [ 1.0, 5.0, 10.0 ],
| "g": [ "A", "B", "C" ]
| },
| "mapping": {
| "x": "x",
| "y": "y",
| "color": "g",
| "size": "s"
| },
| "theme": {
| "flavor": "darcula"
| },
| "ggtitle": {
| "text": "geom_livemap() + geom_point() + flavour_darcula()"
| },
| "layers": [
| { "geom": "livemap" },
| { "geom": "point" }
| ]
|}
""".trimMargin()

private fun MutableMap<String, Any>.updateTiles(tilesSpec: Map<String, Any>) = apply {
getList("layers")!!.asMaps().first().asMutable().putAll(tilesSpec)
return parsePlotSpec(spec)
}

private fun variadicPath(): MutableMap<String, Any> {
val spec = """
{ "data": { "x": [ 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0 ], "y": [ 0.0, 5.0, 0.0, 10.0, 0.0, 5.0, 0.0, 5.0, 0.0 ], "g": [ 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 2.0, 2.0, 2.0 ], "c": [ 1.0, 17.0, 4.0, 8.0, 3.0, 15.0, 15.0, 2.0, 9.0 ], "s": [ 10.0, 10.0, 10.0, 8.0, 3.0, 9.0, 15.0, 12.0, 9.0 ] }, "mapping": {}, "data_meta": {}, "ggtitle": { "text": "geom_path(aes(x=x, y=y, size=s, color=c))" }, "kind": "plot", "scales": [], "layers": [ { "geom": "livemap", "mapping": {}, "data_meta": {}, "tiles": { "kind": "vector_lets_plot", "url": "ws://localhost:3112", "theme": "color", "attribution": "Map: <a href=\"https://github.com/JetBrains/lets-plot\">\u00a9 Lets-Plot</a>, map data: <a href=\"https://www.openstreetmap.org/copyright\">\u00a9 OpenStreetMap contributors</a>." }, "geocoding": { "url": "http://localhost:3012/map_data/geocoding" } }, { "geom": "path", "mapping": { "x": "x", "y": "y", "size": "s", "color": "c" }, "tooltips": { "formats": [], "lines": [ "x" ] }, "data_meta": {} } ], "metainfo_list": [] }
{
"data": {
"x": [ 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0 ],
"y": [ 0.0, 5.0, 0.0, 10.0, 0.0, 5.0, 0.0, 5.0, 0.0 ],
"g": [ 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 2.0, 2.0, 2.0 ],
"c": [ 1.0, 17.0, 4.0, 8.0, 3.0, 15.0, 15.0, 2.0, 9.0 ],
"s": [ 10.0, 10.0, 10.0, 8.0, 3.0, 9.0, 15.0, 12.0, 9.0 ]
},
"mapping": {},
"data_meta": {},
"ggtitle": {
"text": "geom_path(aes(x=x, y=y, size=s, color=c))"
},
"kind": "plot",
"scales": [],
"layers": [
{
"geom": "livemap",
"geocoding": {
"url": "http://localhost:3012/map_data/geocoding"
}
},
{
"geom": "path",
"mapping": {
"x": "x",
"y": "y",
"size": "s",
"color": "c"
},
"tooltips": {
"formats": [],
"lines": [ "x" ]
},
"data_meta": {}
}
],
"metainfo_list": []
}
""".trimIndent()
return parsePlotSpec(spec)
}
Expand All @@ -97,12 +129,6 @@ class LiveMap {
{
"geom": "livemap",
"projection": "epsg4326",
"tiles": {
"kind": "vector_lets_plot",
"url": "wss://tiles.datalore.jetbrains.com",
"theme": "dark",
"attribution": "Map: <a href=\"https://github.com/JetBrains/lets-plot\">\u00a9 Lets-Plot</a>, map data: <a href=\"https://www.openstreetmap.org/copyright\">\u00a9 OpenStreetMap contributors</a>."
},
"geocoding": {
"url": "http://10.0.0.127:3020/map_data/geocoding"
},
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,12 @@

package demo.plot.browser.plotConfig

import demo.plot.common.model.plotConfig.LiveMapWithFacet
import demo.plot.common.model.plotConfig.LiveMap

object LiveMapWithFacetBrowser {
object LiveMapBrowser {
@JvmStatic
fun main(args: Array<String>) {
with(LiveMapWithFacet()) {
@Suppress("UNCHECKED_CAST")
with(LiveMap()) {
(PlotConfigBrowserDemoUtil.show(
"LiveMap plot",
plotSpecList(),
Expand Down
2 changes: 2 additions & 0 deletions future_changes.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@
### Changed

### Fixed

- `geom_livemap()`: theme/flavor plot background is not shown [[#857](https://github.com/JetBrains/lets-plot/issues/857)].
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,15 @@ import org.jetbrains.letsPlot.datamodel.svg.dom.SvgPathElement
internal object SvgPathAttrMapping : SvgShapeMapping<SVGPath>() {
override fun setAttribute(target: SVGPath, name: String, value: Any?) {
when (name) {
SvgPathElement.FILL_RULE.name -> {
val fillRule = when (value as? SvgPathElement.FillRule) {
SvgPathElement.FillRule.EVEN_ODD -> javafx.scene.shape.FillRule.EVEN_ODD
SvgPathElement.FillRule.NON_ZERO -> javafx.scene.shape.FillRule.NON_ZERO
null -> null
}
target.fillRule = fillRule
}

SvgPathElement.D.name -> {
// Can be string (slim path) or SvgPathData
val pathStr = when (value) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,12 @@ import org.jetbrains.letsPlot.core.plot.builder.presentation.LabelSpec
import org.jetbrains.letsPlot.core.plot.builder.presentation.Style
import org.jetbrains.letsPlot.core.plot.builder.tooltip.HorizontalAxisTooltipPosition
import org.jetbrains.letsPlot.core.plot.builder.tooltip.VerticalAxisTooltipPosition
import org.jetbrains.letsPlot.datamodel.svg.dom.SvgElement
import org.jetbrains.letsPlot.datamodel.svg.dom.SvgNode
import org.jetbrains.letsPlot.datamodel.svg.dom.SvgRectElement
import org.jetbrains.letsPlot.datamodel.svg.dom.*
import org.jetbrains.letsPlot.datamodel.svg.event.SvgEventHandler
import org.jetbrains.letsPlot.datamodel.svg.event.SvgEventSpec
import org.jetbrains.letsPlot.datamodel.svg.style.StyleSheet

class PlotSvgComponent constructor(
class PlotSvgComponent(
private val title: String?,
private val subtitle: String?,
private val caption: String?,
Expand Down Expand Up @@ -135,25 +133,32 @@ class PlotSvgComponent constructor(
private fun buildPlotComponents() {
val overallRect = DoubleRectangle(DoubleVector.ZERO, figureSize)

fun SvgPathDataBuilder.rect(rect: DoubleRectangle) = apply {
moveTo(rect.left, rect.top)
lineTo(rect.left, rect.bottom)
lineTo(rect.right, rect.bottom)
lineTo(rect.right, rect.top)
lineTo(rect.right, rect.top)
}

val plotTheme = theme.plot()
if (plotTheme.showBackground()) {
add(SvgRectElement(overallRect).apply {
strokeColor().set(plotTheme.backgroundColor())
strokeWidth().set(plotTheme.backgroundStrokeWidth())
fillColor().set(plotTheme.backgroundFill())
if (containsLiveMap) {
// Don't fill rect over livemap figure.
fillOpacity().set(0.0)
} else {
// Previously there was a fix for JFX here:
// if the background color has no transparency - set its opacity to 0.99.
// Now jfx-mapper will fix it in SvgShapeMapping.
}
})
val background: SvgPathElement? =
when (plotTheme.showBackground()) {
true -> SvgPathElement()
false -> null
}

background?.let {
it.fillRule().set(SvgPathElement.FillRule.EVEN_ODD)
it.strokeColor().set(plotTheme.backgroundColor())
it.strokeWidth().set(plotTheme.backgroundStrokeWidth())
it.fillColor().set(plotTheme.backgroundFill())
add(it)
}

if (DEBUG_DRAWING) {
drawDebugRect(overallRect, Color.MAGENTA, "MAGENTA: overallRect")
val backgroundPath: SvgPathDataBuilder? = when (plotTheme.showBackground()) {
true -> SvgPathDataBuilder().rect(overallRect)
false -> null
}

// -------------
Expand Down Expand Up @@ -236,6 +241,15 @@ class PlotSvgComponent constructor(
if (DEBUG_DRAWING) {
drawDebugRect(geomInnerBoundsAbsolute, Color.ORANGE, "ORANGE: geomInnerBoundsAbsolute")
}

if (containsLiveMap) {
// Add a hole for a map
backgroundPath?.rect(geomInnerBoundsAbsolute)
}
}

if (background != null && backgroundPath != null) {
background.d().set(backgroundPath.build())
}

val geomAreaBounds = figureLayoutInfo.geomAreaBounds
Expand Down
Loading

0 comments on commit 7d3fddb

Please sign in to comment.