Search code examples
visualizationaltairvega-litevega

Altair: How to make scatter plot aligned with image background created by mark_image?


I'm looking for a working example to have a .png picture as the background of a scatter chart. Currently, I use mark_image to draw the background image:

source = pd.DataFrame.from_records([
    {"x":  0, "y": 0,
     "img": "http://localhost:8888/files/BARTStreamlit/assets/BARTtracksmap.png?_xsrf=2%7Ce13c35be%7Ce013c83479b892363239e5b6a77d97dc%7C1652400559"}
])

tracksmap = alt.Chart(source).mark_image(
    width=500,
    height=500
).encode(
    x='x',
    y='y', 
    url='img'
)

tracksmap

Here is the resulted image drown: enter image description here

and draw the scater chart,

chart = alt.Chart(maptable).mark_circle(size=60).encode(
    x= 'x',
    y= 'y',
    tooltip=['short_name', 'ENTRY']
).interactive()

chart

I have scaled the x, y channel values for the scatter chart to be in the range of [0, 500]. 500 is the width and height of the background image that I guessed.

Here is the resulted scatter plot: enter image description here

then I combined the two chart with layer mechanism:

geovizvega = alt.layer(tracksmap, chart)
geovizvega

resulting the following: enter image description here

The two charts do not align. I'd like to have the scatter dots aligning with the tracks on the background image. How can I achieve that?

To have them aligned, I might need to have the background image's top left corner at the coordinates (0, 0), how can I achieve that? (It seems that the x, y channel values for mark_image is the coordinates of the center of the image? With accurate definition of the x, y channel values, it might be possible to calculate the proper value of x, and y for the top left coroner to be at (0, 0)).

I might need to to have precise dimension of the background image. How?

My above approach may not be the right one. Please show me a working example.


Solution

  • Yes, if you change the values of x and y in your image plot to something like y=-200 and x=200, the image should be more centered in the scatter plot.

    You can also change the anchor point of the image using align and baseline:

    import altair as alt
    import pandas as pd
    
    source = pd.DataFrame.from_records([
          {"x": 2, "y": 2, "img": "https://vega.github.io/vega-datasets/data/7zip.png"}
    ])
    
    imgs = alt.Chart(source).mark_image(
        width=100,
        height=100
    ).encode(
        x='x',
        y='y',
        url='img'
    )
    
    imgs + imgs.mark_circle(size=200, color='red', opacity=1)
    

    enter image description here

    imgs = alt.Chart(source).mark_image(
        width=100,
        height=100,
        align='right',
        baseline='top'
    ).encode(
        x='x',
        y='y',
        url='img'
    )
    
    imgs + imgs.mark_circle(size=200, color='red', opacity=1)
    

    enter image description here

    After this, you would still need to change the dimensions of the chart so that it has the same size as the image. The default is width=400 and height=300. You can get the dimensions of your image in most image editing software or using the file <imagename> command (at least on linux). But even after getting these dimensions, you would have to do some manual adjustments due to axes taking up some of that space in the chart.