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