Plotly: How to set a manual bounding box for a Scattergeo?

Viewed 2075

I'm trying to calculate the mean center of population in the U.S. for a variety of variables, so my first step was to compute them for the last 10 years of population by county and see if they match up against the Census maps of this figure (at least roughly, since the Census is using a more granular geographic resolution.) As you'd expect, it's a sort of extremely delayed Manifest Destiny:

enter image description here

(The formula for calculating these points based on many population centers is on page 2 here, if anyone is curious.)

I'm migrating from R/RStudio to Jupyter, using pandas, plotly, numpy, etc. I'm normally a JavaScript engineer, but I'm comfortable with Python and like it quite a bit more than R! I was able to calculate what appear to be similar points, using the airport map example in this fantastic tutorial on plotly.graph_objects.Scattergeo, but I'm stuck at a national resolution so I can't tell how well I'm doing:

enter image description here

I can manually zoom, of course, but I'd really like to understand how to focus on only Missouri by default. I found this useful CodePen, but since there are so many different excellent mapping tools in Python I'm not sure how to port over the JS.

My current map, based on the above tutorial, sets up the basic plot and then modifies the layout's scope in a second line -- I'm not certain if this is standard for plotly, or just a convenience.

As much as I admire Python, going straight to the docs for update_layout is a bit of a rabbit hole :) I don't mind dictating the lat/lng bounds of the viewport, though it would neat to just say "Missouri." I realize this is simple -- just a bit of a learning curve.

import pandas as pd
import plotly.graph_objects as go

meanCenters = pd.read_json('{"year": {"0": 2010, "1": 2011, "2": 2012, "3": 2013, "4": 2014, "5": 2015, "6": 2016, "7": 2017, "8": 2018, "9": 2019}, "lat": {"0": 37.52908501121699, "1": 37.51719645600817, "2": 37.50264465332917, "3": 37.489564543614605, "4": 37.47311004581999, "5": 37.45358003505096, "6": 37.43623735876329, "7": 37.423475510154134, "8": 37.4121869670822, "9": 37.40021167050047}, "lng": {"0": -92.1522086893934, "1": -92.17800419530532, "2": -92.20484620692078, "3": -92.23312370193283, "4": -92.26617930383293, "5": -92.30498885605378, "6": -92.3395596061908, "7": -92.36398254029461, "8": -92.38362683728195, "9": -92.40337680455285}}')

fig = go.Figure(data=go.Scattergeo(
    lon = meanCenters['lng'],
    lat = meanCenters['lat'],
    mode = 'markers'
))
fig.update_layout(
    geo_scope='usa',
    height=600
)
fig.show()

Data is included above -- thx, @vestland! -- but reprinted here for readability

year    lat lng
2010    37.52908501121699   -92.1522086893934
2011    37.51719645600817   -92.17800419530532
2012    37.50264465332917   -92.20484620692078
2013    37.489564543614605  -92.23312370193283
2014    37.47311004581999   -92.26617930383293
2015    37.45358003505096   -92.30498885605378
2016    37.43623735876329   -92.3395596061908
2017    37.423475510154134  -92.36398254029461
2018    37.4121869670822    -92.38362683728195
2019    37.40021167050047   -92.40337680455285
1 Answers

I haven't found a way to specify how to show a geographical area like Missouri directly, but you can specify how to focus on your data using:

fig.update_geos(fitbounds='locations')

Which for your data sample will give you this:

enter image description here

So this simple approach would make much more sense for larger datasets that would include a bigger piece of the US map. But a very nice thing here is that you can build your figure using px.Line() and then add to that using, for example:

fig.add_traces(go.Scattergeo(lat=[41,36], lon=[-96,-89]

And those coordinates just so happens to make the outline of the map include Missouri, so you'll get this (the markers are set to 100% transparent):

enter image description here

And if I'm understanding you correctly, this is more or less what you're looking for? Perhaps the best take-away from this suggestion is that you can build a figure using using px.express, add data using fig.add_traces(go.Scattergeo()) and then edit the geographical features using fig.update_geos() and not only fig.update_layout().

Here's a complete code snippet to reproduce the second figure:

import plotly.express as px
import plotly.graph_objects as go
meanCenters = pd.read_json('{"year": {"0": 2010, "1": 2011, "2": 2012, "3": 2013, "4": 2014, "5": 2015, "6": 2016, "7": 2017, "8": 2018, "9": 2019}, "lat": {"0": 37.52908501121699, "1": 37.51719645600817, "2": 37.50264465332917, "3": 37.489564543614605, "4": 37.47311004581999, "5": 37.45358003505096, "6": 37.43623735876329, "7": 37.423475510154134, "8": 37.4121869670822, "9": 37.40021167050047}, "lng": {"0": -92.1522086893934, "1": -92.17800419530532, "2": -92.20484620692078, "3": -92.23312370193283, "4": -92.26617930383293, "5": -92.30498885605378, "6": -92.3395596061908, "7": -92.36398254029461, "8": -92.38362683728195, "9": -92.40337680455285}}')

fig = px.line_geo(lat=meanCenters['lat'], lon=meanCenters['lng'])

fig.add_traces(go.Scattergeo(lat=[41,36], lon=[-96,-89],
                             mode = 'markers',
                             marker = dict(size = 2,color = 'rgba(0, 0, 0, 0)'),
                             name='Missouri'))

fig.update_geos(visible=True, resolution=50, scope="north america",
                fitbounds='locations',
                showcountries=True, countrycolor="Black",
                showsubunits=True, subunitcolor="grey")

fig.show()
Related