[BUG]: Image layer on a map trace renders a little north of where coordinates says it should
@camdecoster arbeitet bereits daran.
Seit 15.9.2026.
Bewertung
Dieses Issue wurde noch nicht bewertet.
Beschreibung
Hi, thanks for maintaining this library. I ran into something while building a small feature that draws an interpolated raster on top of a map (MapLibre-based) figure as an image layer, and I wanted to check whether this is expected or might be a bug somewhere in how the image source's coordinates get turned into screen positions. Found this while using plotly.py, but hope this is the right place.
Description
When I add an image via layout.map.layers with sourcetype: "image", the image renders slightly north of the position its coordinates describe. It's small, but it's consistent and I can reproduce it reliably. A static export of the exact same figure through kaleido does not show this offset - only the live, interactive rendering does. That's actually how I first noticed it: I was checking my own coordinate math against a kaleido render, everything lined up, and then it looked off again as soon as I opened the figure in a browser.
Versions
plotly(Python): 6.9.0- I tried both
include_plotlyjs="cdn"andinclude_plotlyjs=True(the bundle that ships with 6.9.0) and got the same result either way, so it doesn't seem to be tied to a particular plotly.js build. - Browser: Chromium
- OS: macOS
Steps to reproduce (Minimal example)
Needs plotly, numpy, and pillow.
import base64
import io
import numpy as np
import plotly.graph_objects as go
import plotly.io as pio
from PIL import Image
# A small raster: mostly transparent white, with a 2-row-thick red band
# painted across the exact middle row. Row 0 is the top of the image (north),
# matching the coordinate order below.
ny, nx = 100, 100
rgba = np.full((ny, nx, 4), 255, dtype=np.uint8)
rgba[..., 3] = 160
mid = ny // 2
rgba[mid - 1 : mid + 1, :] = [255, 0, 0, 255]
buffer = io.BytesIO()
Image.fromarray(rgba).save(buffer, format="PNG")
data_uri = "data:image/png;base64," + base64.b64encode(buffer.getvalue()).decode()
WEST, SOUTH, EAST, NORTH = 134.5, 32.0, 142.5, 40.5
# Where the red band actually sits, by construction.
true_lat = NORTH - (mid + 0.5) / ny * (NORTH - SOUTH)
figure = go.Figure()
figure.update_layout(
map={
"style": "carto-positron",
"center": {"lat": (SOUTH + NORTH) / 2, "lon": (WEST + EAST) / 2},
"zoom": 5.2,
"layers": [
{
"sourcetype": "image",
"source": data_uri,
"coordinates": [[WEST, NORTH], [EAST, NORTH], [EAST, SOUTH], [WEST, SOUTH]],
"opacity": 1.0,
"below": "traces",
}
],
},
width=1200,
height=1000,
title="Red band should sit on the green line, not above it",
)
# A second, independent way of marking the same latitude, so there's
# something to compare the image against that isn't derived from the image
# layer itself.
figure.add_trace(
go.Scattermap(
lat=[true_lat, true_lat],
lon=[WEST, EAST],
mode="lines",
line={"color": "lime", "width": 1},
hoverinfo="skip",
showlegend=False,
)
)
pio.write_html(figure, "repro.html", include_plotlyjs=True)
Opening repro.html, I'd expect the red band to sit right on top of the green line, since both mark the same latitude computed two different ways. Instead the red band shows up a bit north of it - see the attached screenshot (plotly_issue_repro_minimal.png).
If it helps, pio.write_image(figure, "repro_kaleido.png") (needs kaleido) renders this correctly, no offset - which is what made me think this is specific to the live MapLibre GL rendering path rather than something wrong with the coordinates I'm passing in.
Trying to narrow it down a little further
I wasn't sure if this was a fixed number of pixels (which would point at something like a CSS/layout offset) or a fixed number of degrees (which would point at the coordinate conversion itself), so I measured it two ways:
- Read
map.project([lon, lat])from the live MapLibre instance for the reference latitude, as a pixel ground truth. - Located the actual rendered red band in a screenshot of the page.
At zoom: 5.2 the offset came out to about 13 device pixels. At zoom: 7.5, same figure otherwise, it was about 62.5 device pixels. That ratio (about 4.8x) is close to the ratio of pixels-per-degree between those two zoom levels (about 4.9x), which suggests the offset is roughly constant in latitude (somewhere around 0.1° at this location) rather than constant in screen pixels. I haven't checked whether it varies with latitude or with the image's aspect ratio - happy to dig further if that would be useful.
Why I noticed / why it might matter for others
I'm drawing an interpolated field (station observations, not tiles) under some markers, and the markers and the raster underneath them are now visibly not quite aligned in the interactive view, even though they line up fine in a static export of the same figure. I've attached a screenshot from that real case too (plotly_issue_real_data_example.png) with the true bounds drawn in black for reference, in case it's useful to see this on something other than the synthetic example above.
Let me know if there's anything else I can provide, or if I've missed something obvious here - happy to help track it down further if useful.
Screenshots
- Vorherrschende Sprache
- JavaScript
- Sterne
- 18.3k
- Forks
- 2k
- Ø Merge
- 2 T. 10 Std.
- Gemergte PRs (30 T.)
- 30
Beitragsleitfaden
Erste Schritte
- Lesen Sie das ganze Issue und danach den Beitragsleitfaden des Projekts.
- Schreiben Sie ins Issue, dass Sie es übernehmen — das erspart doppelte Arbeit.
- Forken Sie das Repository und arbeiten Sie in einem Branch.
- Öffnen Sie einen Pull Request, der die Issue-Nummer nennt.
Mehr aus plotly/plotly.js
-
chore P3 plotly-internal size: 3 task
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 77/100
-
chore P1 plotly-internal size: 1 task
Schwierigkeit 1/5 Unter einer Stunde Anfängerfreundlichkeit 82/100
-
chore P3 plotly-internal size: 1 task
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 65/100
-
bug
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 65/100
-
bug infrastructure P2
Schwierigkeit 1/5 Unter einer Stunde Anfängerfreundlichkeit 65/100
Alle Issues in plotly/plotly.js
Ähnliche Issues
-
bug confirmed issue
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 75/100
open-webui/open-webui#30750 · 1 Kommentar ·
-
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 75/100
-
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 70/100
-
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 75/100
-
Mend: dependency security vulnerability untriaged
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 70/100