Mapbox coordinates for visible extent are incorrect and change with browser zoom
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 24.4k
- Forks
- 2.3k
- Avg merge
- 2d 7h
- Merged PRs (30d)
- 13
Description
Please let me know if this issue is better placed in https://github.com/plotly/plotly.py/issues !
Thank you for maintaining such a useful tool :)
Describe your context
dash 1.18.1
dash-bootstrap-components 0.11.1
dash-core-components 1.14.1
dash-html-components 1.1.1
dash-renderer 1.8.3
dash-table 4.11.1
- OS: macOS Catalina
- Browser Reproduced in Chrome (87.0.4280.141) and Firefox (84.0.2)
Describe the bug
The mapbox._derived.coordinates properties accessed through relayoutData of a Scattermapbox figure interacts with the zoom level of a browser in an unexpected way, reporting an incorrect value for the maximum longitude visible in the map extent unless the browser zoom is set to 50%.
Expected behavior
I've tried to create a minimal reproducible example below illustrating my goal. I would like to plot some points on a map and show more detail in a table below, only for the plots that are currently visible in the map. I use a callback to access data['mapbox._derived']['coordinates'] from relayoutData to do the filtering, but have found that the bounding box reported is much larger than what is actually visible.
If I zoom out in my browser the discrepancy becomes smaller, until it looks correct at 50% zoom.
I've played with settings for the layout like autosize=True|False, explicitly setting and unsetting width and height, and fluid=True|False to try isolate what is causing the discrepancy without luck. It seems like the figure thinks the map is a lot bigger than it actually is, but gets the location of the top left corner correct. There's a reasonable chance this is not a bug and I've missed some important step or scaling setting, but couldn't find much on the docs on this topic.
Screenshots
https://user-images.githubusercontent.com/1023566/104812997-293b6480-57fe-11eb-9640-1af87e3cd1a7.mp4
Example code
import dash
import dash_core_components as dcc
import dash_html_components as html
import dash_table
import pandas as pd
from dash.dependencies import Input, Output
import dash_bootstrap_components as dbc
import plotly.graph_objects as go
app = dash.Dash(__name__)
df = pd.DataFrame(
{'location.latitude': [51.48717, 51.48717, 51.514085, 51.514085],
'location.longitude': [-0.116738, -0.07087, -0.116738, -0.07087]}
)
map_graph = dcc.Graph(
id='map_graph',
figure=go.Figure(
data=go.Scattermapbox(
lat=df['location.latitude'],
lon=df['location.longitude'],
mode='markers',
marker=go.scattermapbox.Marker(
size=14
)
),
layout=go.Layout(
showlegend=False,
mapbox=dict(
accesstoken=open(".mapbox_token").read(),
bearing=0,
center=dict(
lat=51.5,
lon=0
),
pitch=0,
zoom=10
),
height=600,
margin={'l': 0, 'r': 0, 'b': 0, 't': 0, 'pad': 4},
)
)
)
app.layout = dbc.Container(children=[
html.H1(children='Map example'),
html.Hr(),
map_graph,
html.Div(id='my-output'),
dash_table.DataTable(
id='output_table',
columns=[{"name": i, "id": i} for i in df.columns],
data=df.to_dict('records'),
),
html.Table([
html.Tr([html.Td('Lat min:'), html.Td(id='lat_min')]),
html.Tr([html.Td('Lat max:'), html.Td(id='lat_max')]),
html.Tr([html.Td('Lon min:'), html.Td(id='lon_min')]),
html.Tr([html.Td('Lon max:'), html.Td(id='lon_max')])
])],
fluid=True
)
@app.callback(
Output(component_id='output_table', component_property='data'),
Output(component_id='lat_min', component_property='children'),
Output(component_id='lat_max', component_property='children'),
Output(component_id='lon_min', component_property='children'),
Output(component_id='lon_max', component_property='children'),
Input(component_id='map_graph', component_property='relayoutData')
)
def map_extents_changed(data):
try:
print(data)
coords = data['mapbox._derived']['coordinates']
lon_min = coords[0][0]
lon_max = coords[1][0]
lat_min = coords[2][1]
lat_max = coords[1][1]
return(
df.loc[(df['location.longitude'] > lon_min) &
(df['location.longitude'] < lon_max) &
(df['location.latitude'] > lat_min) &
(df['location.latitude'] < lat_max)].to_dict('records'),
lat_min,
lat_max,
lon_min,
lon_max
)
except:
return(df.to_dict('records'), 0, 0, 0, 0)
if __name__ == '__main__':
app.run_server(debug=True)
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Run the supplied Dash example with the stated dash and browser versions, then inspect relayoutData and mapbox._derived.coordinates at several browser zoom levels. Compare the reported bounds with the visible map and determine whether the discrepancy is in the example's coordinate handling or the map rendering; done means the bounds match the visible extent across browser zoom levels.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- data-visualization, frontend
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100