Use mapbox to place images with PlotlyJS

I want to create a map with PlotlyJS that includes lines and grouped/stacked bar charts. I’ve asked on stackoverflow how to achieve this in general and got a solution that deploys mapboxes. Now I’m trying to convert this into Julia and can’t get the image to appear:

using PlotlyJS, DataFrames, CSV
# create trace for lines (not immediately releveant for mapbox problem)
nm = tempname()
url = "https://raw.githubusercontent.com/plotly/datasets/c34aaa0b1b3cddad335173cb7bc0181897201ee6/2011_february_aa_flight_paths.csv"
download(url, nm)
cord = CSV.read(nm,DataFrame)

M = maximum(cord[:,:cnt])

trace_data = [scattergeo(;locationmode="USA-states",
    lon = [cord[1,:start_lon], cord[1,:end_lon]],
    lat = [cord[1,:start_lat], cord[1,:end_lat]],
    mode = "lines",
    line_width = 2,
    line_color = "red",
    opacity = cord[1,:cnt]/M
)];


# create mapbox data
images_1 = [attr(sourcetype = "raster", source = rand(4, 4), coordinates =  [cord[1,:start_lat],cord[1,:start_lon]])]

# create layout and plot
layout = Layout(;showlegend=false, mapbox_style="carto-positron", mapbox_zoom=3, mapbox_layers = images_1)
plot(trace_data, layout)

For testing, I’ve also tried to use a geojson as as an input but did not get a result either. Below is the corresponding code and the content of testGeoJSON.GEOJSON.

images_2 = [attr(sourcetype = "geojson", source = "testGeoJSON.GEOJSON")]
{
  "type": "Feature",
  "geometry": {
    "type": "Point",
    "coordinates": [125.6, 10.1]
  },
  "properties": {
    "name": "Dinagat Islands"
  }
}

Many thanks!

PS: I’m also greatful for any help on the next step, which is how to convert a PlotlyJS plot into an input for mapboxes. I figure that source = plot(data,layout) is probably not the right way to go.

The steps to get a choropleth mapbox with stacked bars:

  1. For stacked bars you need a long-form data frame. Here I use a minimalistic example:
nation	medal	count
String15	String7	Int64
1	South Korea	gold	24
2	China	gold	10
3	Japan	gold	9
4	South Korea	silver	13
5	China	silver	15
6	Japan	silver	12
7	South Korea	bronze	11
8	China	bronze	8
9	Japan	bronze	12
  1. from such a dataframe you can get stacked bars, as follows:
using PlotlyJS, CSV, DataFrames, Base64
df=CSV.read("long-medals.csv", DataFrame)
fig = Plot(df, kind="bar", x=:nation, y=:count, color=:medal, 
              Layout(barmode="stack", showlegend=false,
                     xaxis_visible=false, yaxis_visible=false,
                     bargap=0,
                     margin= attr(l= 0, r= 0, t= 0, b= 0),
                     height=100, width=100,
                     paper_bgcolor="rgba(0,0,0,0)",
                     plot_bgcolor="rgba(0,0,0,0)"
    )
)
savefig(fig, "stacked.png")
  1. Encode the png file as a String, via base64encode, to be passed as source for mapbox_layer:
vecuint8 = read("stacked.png") 
bs64 = base64encode(vecuint8)
imgsource= "data:image/png;base64,"*bs64    
  1. Get data for the dataframe to be used in the choroplethmapbox definition:
using JSON, HTTP
url= "https://gist.githubusercontent.com/hrbrmstr/94bdd47705d05a50f9cf/raw/0ccc6b926e1aa64448e239ac024f04e518d63954/asia.geojson"
result = HTTP.request("GET", url)
jsondata = JSON.parse(String(result.body))

countries = ["China", "Korea", "Japan"]  
isoc= String[]
for co in countries
    for feat in jsondata["features"]
        if co == feat["properties"]["name"]
             push!(isoc, feat["properties"]["iso_a3"]) 
        end
    end    
end 
print(isoc)
dfchoro = DataFrame(iso_a3=isoc, val= [1, 1, 1])     
  1. Define the choroplethmapbox:
url  =  "https://gist.githubusercontent.com/hrbrmstr/94bdd47705d05a50f9cf/raw/0ccc6b926e1aa64448e239ac024f04e518d63954/a
sia.geojson"
q = Plot(choroplethmapbox(geojson = url,
                          featureidkey = "properties.iso_a3",
                          locations = dfchoro.iso_a3,
                          z=dfchoro.val,
                          colorscale=[[0, "#d3d3d3"], [1, "#d3d3d3"]], showscale=false,
                          marker=attr(opacity=0.85, line=attr(width=0.5, color="black"))),
          Layout(mapbox =attr(center=attr(lon =100.61, lat=40.04),
                                          zoom=1.5, style="open-street-map"),
                 mapbox_layers=[
                               attr(sourcetype= "image",           
                                    source=imgsource,
                                    coordinates=  [[104, 38], [110, 38], [110, 29], [104, 29]])]))  

stacked-bars-map
Following these steps and using your data you can get the map decorated with stacked bars, and similarly with any other images.

1 Like

Many thanks! I will mark the solution as soon as I had the time to test.

1 Like

How can I get this to work, if the GeoJSON is a local file instead of an url? I tried to exchange the url with a local directory that I placed the same file in but had no success.

Say you’ve downloaded the file instead as

## Alternative to 4:
using JSON, Downloads
url= "https://gist.githubusercontent.com/hrbrmstr/94bdd47705d05a50f9cf/raw/0ccc6b926e1aa64448e239ac024f04e518d63954/asia.geojson"
filename = "asia.geojson"
Downloads.download(url, filename)
jsondata = JSON.parsefile(filename)

then instead of
q = Plot(choroplethmapbox(geojson = url,
do
q = Plot(choroplethmapbox(geojson = attr(type=jsondata["type"], features=jsondata["features"]),

1 Like

@lgo
A local geojson file can be read as a JSON file: LightOSM : reading GeoJSON files

1 Like

Thanks to your help I was able to achieve what I had in mind but I have a new problem and thought it might be more appropriate to ask here instead of starting a new thread because this closely relates to the original problem.

I mainly design the map to put it on slides or in a paper but the aspect ratio of the mapbox map is not really suited for that. So ideally I would like to use a different projection but the mapbox layout does not a have a corresponding option. On the other hand, I cannot switch to a geo map, because I can’t find a way to place my graph than. The geo layout does not have such an option.

So far, my best idea is some workaround that just builds on layering graphics and implies loosing some of the plotly map features, like hoovering for example.