Repository navigation
[BUG]: Default zoom and center for scattermap traces does not show all points #7674
Description
Activity
@palmerusaf this is the issue!
- addedgood first issuesuitable for newcomerssuitable for newcomersP2considered for next cycleconsidered for next cycle
on Dec 17, 2025 @emilykl
After stepping through the code I discovered that ultimately these defaults are being set from static values located insrc/plots/map/layout_attributes.js. The defaults are coordinates (0,0) for center and 1 for zoom. I was able to confirm this by changing the lon default to -98 and observing the map center over the USA rather than Africa.These defaults get applied with the
coerce('center.lon');callback in thehandleDefaultshelper function inside modulesrc/plots/map/layout_defaults.js. We don't have access to data in thehandleDefaultsscope. This function is passed as a callback tohandleSubplotDefaultswhich is a function shared by other seven other plots. Looking at how other plots implementhandleDefaultsit looks like there may be some flexibility change the signature and inject data under theoptsparameter without causing cascading changes to other plots.One way we could fit all the data points is to get the north, south, east, and west bounds. North and south are easy since the map doesn't wrap at the poles. You could get the min bounds for west/east by sorting all lon points then using the sliding window method to find the min distance that includes all points. This would make
handleDefaultsO(nlogn)for the sorting though. There might be a greedy algo that will run inO(n), but I'm not sure if it would always give you a min bounding box for all coordinates.Once you have the bounds you could set those as defaults instead of center/zoom or use them to calculate center/zoom defaults.
What do you think?
@palmerusaf This is great, thanks for looking into it thoroughly. Happy New Year!
It's a good point that calculating the centroid of a set of geo locations isn't trivial. I suppose we probably shouldn't put ourselves in the position of manually performing that calculation during
supplyDefaults, but that would be the place to put it, if we want to. (I think we might have access to the data, inside eithercontainerInorcontainerOut, and you're right that we can also change the function signature if necessary.)The ideal situation would be to just let MapLibre handle it, but I'm not sure it's capable. Could you verify what happens if
undefined/nullor no value at all is passed forzoomandcenterwhen initializing the map, and whether it's different from the current behavior?One other thing to check is whether MapLibre itself, or another geo library we already bundle, offers a utility for finding the centroid and/or the correct zoom level for a given set of points.
I suppose we probably shouldn't put ourselves in the position of manually performing that calculation during
supplyDefaultsDespite the fact that this does set some default values, normally we don't allow
supplyDefaultsto do ANYTHING that loops over all the data in a trace, becausesupplyDefaultshappens so often and a loop like that can take a long time. This is analogous to axis qutorange in cartesian plots, which we do in the calc step and only when we think the data has changed. That said that pattern is annoying - it requires us to push those calculated values back into the input figure so that we can use them in later renders. So I bet there's a better pattern we can use that does do this calculation duringsupplyDefaultsbut caches the result - the key then being to properly determine when to bust the cache.Thanks @alexcjohnson, that makes sense. So if we can come up with a reasonable caching pattern, then you think it would be reasonable to do some kind of zoom/center calculation in
supplyDefaults?Reacted by Alex Johnson@emilykl
I just tried every variation of null/undefined and the behavior is the same as omitting the parameters outright.I've been researching the MapLibre docs, but I haven't found anything useful yet. It seems most of the API deals with exposing map controls rather than calculations. This is the right place to look right?
We are using Turf.js already which does a lot of map calculations, but it seems to struggle when crossing the anti-meridian. I even found this TODO that suggests rolling our own bounding box algo. This has been an open issue for a while. It even has its own label, so it's not just bbox that turf struggles with.
I've been playing around with the code to see what I can come up with. The initial results look promising.
- Before
- After
It even seems to work along the anti-meridian.
But it still needs some work. I think the main issue is that I'm using linear measurements for center/zoom scaling, and the map isn't linear in the y-direction. We probably need some of that fancy map math for the scaling, or at least a library that uses some.
Here is a link to the code.
Update it looks like maplibre does have a fitBounds method on the map object, but the map object isn't created until after defaults are set so you don't have access to it in the default handlers.
https://maplibre.org/maplibre-gl-js/docs/API/classes/Map/#fitboundsIt does however look like you can do this in the constructor if I'm reading this right. So we'd only have to come up with min/max lat/lon. Note the we're already using a variable
boundsas an alias formaxBounds, but it looks like the mapLibreboundsdoes the zooming/centering we need whereasmaxBoundslimits the moveable area.
https://github.com/maplibre/maplibre-gl-js/blob/b8f34428adc052583c7197daaf11b5e6deb54343/src/ui/map.ts#L311
Description
When creating a
scattermaptrace, users have the option to set thelayout.map.zoomandlayout.map.centeroptions to control which section of the map is shown.If
zoomandcenterare not provided, the trace should default to showing all datapoints; however this is not what happens -- sometimes datapoints are outside the map area.@ndrezn reported a similar issue which seems to be caused by plotly.py, but this one is reproducible in plotly.js.
Codepen
This Codepen demonstrates the issue:

https://codepen.io/emilykl-code/pen/bNpyXqQ
Trondheim is one of the cities plotted, but it's not visible in the initial map view; you need to pan the map northward to see it.
Expected behavior
At a minimum, the initial map zoom and center should show all data points.
It's also probably reasonable to choose a default zoom such that the data points mostly fill the plot area.
Notes
Questions to answer: