plotly / plotly/plotly.js

[BUG]: marker.colorbar.tickfont.textcase='normal' blanks scattermap on initial render, but Plotly.restyle renders correctly

オープン
#7,869 コメント 4 件 リアクション 0 件 担当者 0 名 GitHub で見る

まだ誰も着手していません。

bug good first issue good for agent P3 size: 3
主要言語
JavaScript
スター
18.3k
フォーク
2k
平均マージ
2日 12時間
マージ済み PR(30日)
28

説明

Description

When initializing a Plotly Scattermap figure with
marker.colorbar.tickfont.textcase='normal', the initial render can fail to draw the
OpenStreetMap tiles and map-layer geometry. The figure area is mostly blank, with only
right-side numeric tick labels and the OpenStreetMap attribution visible.

However, if the same base figure is rendered first and the same property value is
then applied dynamically through Plotly.restyle, the map renders correctly: the OSM
tiles appear, the blue filled map layer is visible, and the cyan marker is drawn.

The setting is accepted by plotly.py and emitted as a valid trace property, so this
appears to be an initial-render/update-time rendering discrepancy involving
Scattermap marker colorbar tick font defaults rather than an invalid Python-side
property assignment.

Screenshots/Video
Image Image
Steps to reproduce
import io
import time

import plotly
import plotly.graph_objects as go
import plotly.io as pio
from PIL import Image, ImageChops
from playwright.sync_api import sync_playwright

print(f"Plotly version: {plotly.__version__}")


def make_fig():
    fig = go.Figure(
        go.Scattermap(
            mode="markers",
            lon=[-73.605],
            lat=[45.51],
            marker={"size": 20, "color": ["cyan"]},
        )
    )

    polygon = [
        [
            [
                [-73.62, 45.50],
                [-73.59, 45.50],
                [-73.59, 45.53],
                [-73.62, 45.53],
                [-73.62, 45.50],
            ]
        ]
    ]

    fig.update_layout(
        map={
            "style": "open-street-map",
            "center": {"lon": -73.6, "lat": 45.5},
            "zoom": 12,
            "layers": [
                {
                    "source": {
                        "type": "FeatureCollection",
                        "features": [
                            {
                                "type": "Feature",
                                "geometry": {
                                    "type": "MultiPolygon",
                                  "coordinates": polygon,
                                },
                            }
                        ],
                    },
                    "type": "fill",
                    "below": "traces",
                    "color": "royalblue",
                }
            ],
        },
        margin={"l": 0, "r": 0, "b": 0, "t": 0},
    )
    return fig


fig = make_fig()

# Screenshot 1 / plot1.png: set the value in Python before the first render.
fig.data[0].marker.colorbar.tickfont.textcase = "normal"
assert fig.data[0].marker.colorbar.tickfont.textcase == "normal"
print("Python-side tickfont JSON:", fig.data[0].marker.colorbar.tickfont.to_plotly_json())
html1 = pio.to_html(fig, full_html=True, include_plotlyjs="cdn")


# Screenshot 2 / plot2.png: render first, then apply the same value with JS.
fig.data[0].marker.colorbar.tickfont.textcase = None
html2 = pio.to_html(fig, full_html=True, include_plotlyjs="cdn")
restyle_script = """
<script>
window.addEventListener("DOMContentLoaded", () => {
    const plotEl = document.getElementsByClassName("plotly-graph-div")[0];
    setTimeout(() => {
        Plotly.restyle(plotEl, {"marker.colorbar.tickfont.textcase": "normal"}, [0]);
    }, 1000);
});
</script>
"""
html2 = html2.replace("</body>", restyle_script + "\n</body>")


with sync_playwright() as p:
    browser = p.chromium.launch(
        headless=True,
        args=["--no-sandbox", "--disable-dev-shm-usage", "--disable-gpu"],
    )
    page = browser.new_page(viewport={"width": 1200, "height": 800})

    page.set_content(html1)
    time.sleep(1.5)
    img1 = page.screenshot(path="plot1.png")

    page.set_content(html2)
    time.sleep(2.0)
    img2 = page.screenshot(path="plot2.png")

    browser.close()

diff = ImageChops.difference(
    Image.open(io.BytesIO(img1)).convert("RGB"),
    Image.open(io.BytesIO(img2)).convert("RGB"),
)

print("Saved plot1.png and plot2.png")
assert diff.getbbox() is not None, "Reproducer did not reproduce: images are identical."
Notes

Add info here that doesn't fit in the other sections.

コントリビューションガイド

コントリビューションガイドを開く

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

調査の方向性

提供された Python/Playwright の再現プログラムから始め、初期の Scattermap のレンダリングと、後続の Plotly.restyle 呼び出しを比較します。初期化中の marker colorbar tickfont.textcase の処理を追跡し、両方のパスで OpenStreetMap タイル、マップレイヤーのジオメトリ、シアンのマーカーが正しくレンダリングされれば、Issue は完了とみなします。

索引モデルが issue の本文から書いたものです。

評価

技術スタック
javascript, playwright, python
領域
data-visualization
issue の種類
バグ
難易度
4/5
見積もり時間
3〜5日
活発さ
静か
明瞭さ
おおむね明確
初心者へのやさしさ
50/100

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。