How best to draw county boundaries on a mapbox figure without data

I’m building a Mapbox figure with several traces that can be switched on and off via the legend. Several of the layers are outline (geojson) shapes for counties, zip codes, etc. I’m using choropleth_mapbox traces and set the marker color to blue for the outlines. I set “z” (data) to 0 for all locations. Combined with the default divergent colorscale (with “white” in the mid scale position), this gives me a trace with the county outlines and “nothing” (white with the opacity dialed down) inside the tile. This looks fine and I get nice hover text.

But in the figure legend, this layer shows up with the rainbow auto color scale as its key. How do I make the legend pick up the color set in the marker properties?

I’m using plotly 5.5.0.

Code below:

import json

import plotly.graph_objects as go

# from https://github.com/plotly/datasets/blob/master/geojson-counties-fips.json
COUNTIES_GEO = json.load(open("assets/geojson-counties-fips.json"))
COUNTIES_FEATURES = COUNTIES_GEO["features"]
COUNTIES = [x["id"] for x in COUNTIES_FEATURES]
COUNTY_HOVERS = [x["properties"]["NAME"] for x in COUNTIES_FEATURES]

LEGEND = dict(
    itemclick='toggle',
    itemdoubleclick=False,
    font=dict(family="Noto Sans", size=14),
    title=dict(
        text="<b>SELECT TO VIEW</b>",
        side='top',
    ),
    orientation='v',
    traceorder="reversed",
)

figure = go.Figure()

figure.add_trace(
    go.Choroplethmapbox(
        name="Counties",
        geojson=COUNTIES_GEO,
        locations=COUNTIES,
        z=[0] * len(COUNTIES),
        hoverinfo="text",
        hovertext=COUNTY_HOVERS,
        marker=dict(
            line=dict(color="blue", width=1),
            opacity=0.4,
        ),
        showscale=False,
        showlegend=True,
        visible="legendonly",
    )
)

figure["layout"].update(dict(
    legend=LEGEND,
    mapbox_style="open-street-map",
    mapbox_zoom=3.5,
    mapbox_center=dict(lon=-94.0, lat=38.0),
    autosize=True,
    hovermode="closest",
    margin={'r': 0, 't': 0, 'l': 0, 'b': 0},
))

figure.show(config={'displayModeBar': False})

@ddr

Looking at your code I noticed that you did not set the featureidkey in your Choroplethmapbox. That’s why you get empty counties. featureidkey connects data in geojson with data in locations, i.e. you must have a common id in geojson and locations for each county.

I post here an example to see how you can get the county boundaries, when z is constant. I suggest to set the white colorscale, as in my example, instead of the default diverging colorscale:

import numpy as np
import json
import pandas as pd
import plotly.graph_objs as go

import urllib.request

def read_geojson(url):
    with urllib.request.urlopen(url) as url:
        jdata = json.loads(url.read().decode())
    return jdata 

swiss_url = 'https://raw.githubusercontent.com/empet/Datasets/master/swiss-cantons.geojson'
jdata = read_geojson(swiss_url)



data_url = "https://raw.githubusercontent.com/empet/Datasets/master/Swiss-synthetic-data.csv"
df = pd.read_csv(data_url)
fig=go.Figure(go.Choroplethmapbox(
              name="Cantons",
              geojson=jdata,
              text=df["canton-name"],
              locations=df['canton-id'],
              z=[0]*len(df), 
              featureidkey='properties.id',
       
              colorscale=[[0,"rgb(255,255,255)"], [1,"rgb(255,255,255)"]],
              showscale=False,
              marker=dict(
              line=dict(color="blue", width=1),
              opacity=0.4)))

                  
fig.update_layout(title_text="Test",
                  title_x=0.5,
                  width=900, height=500,
                  mapbox=dict(zoom=6.25,
                           center={'lat': 46.8181877 , 'lon':8.2275124 },
                             style="open-street-map")
                  )

Thanks for the reply!

I know about featureidkey. The default county geojson uses “id” as the key so it works without specifying the parameter. The key here is I don’t want any color inside the county boundaries. I want blue outlines with white/transparent interiors. But I want the color in the LEGEND to match the lines (BLUE). If I use the white color scale as you suggest, I get white (on white bg) in the legend. How do I get the legend to show me BLUE to match the outline color?

@ddr
It seems that I haven’t understood what really you intend to illustrate in your plot, because you say you have several traces (but here is only one). You are referring to layout layers, but did not define any layer in the code you posted. When it comes with such a complex plot it’s impossible to give an advice without the complete code, such that to be able to run it and suggest a solution.

I may not be using terms in a clear way.

The “Counties” trace is meant to draw the outlines of counties on the U.S. map. I don’t have any DATA associated with counties. I just want their outlines and hover names as reference to other traces (choropleth or scatter) on the map. The code above draws the county outlines I want but also draws the interior of each county with white, made partially transparent with opacity = 0.4.

I have two wishes:

  1. I want the interior of the county shapes to be fully transparent AND still show hover text when I point inside that trace.
  2. I want the line for the Counties trace in the legend to be the same “blue” color as the outline on the map. If I set the colorscale to “all white” as you suggest, the legend line is white, not blue like the trace outline.

Is this more clear?

Thanks for considering the problem!
-David

@ddr
As long as your figure.data[0] is a choroplethmapbox, the legend displays, by default, the colorscale (i.e. a small colorbar) as corresponding color.
This setting is out of our control, because plotly.js is responsible for the mapping trace -> legend color
If you print help(go.layout.Legend) you can see that there is no color keyword to be set.

1 Like