Black Lives Matter. Please consider donating to Black Girls Code today.
Dash HoloViews is now available! Check out the docs.

ScatterMapbox - Setting Visible Range

I am using the scattermapbox via the python API and have no trouble creating a plot. However, I am unable to figure out how to set the visible area.

For example, if I am plotting markers on a US map and all of the markers are in Florida, I only want to show the state of Florida (or specific lat/long ranges). But if there are points in Texas and Florida, I will want to show all of the states from Texas to Florida. This works easily with Plotly’s scattergeo by passing the following in the geo dict:

lonaxis=dict(range=(long_min, long_max)),
lataxis=dict(range=(lat_min, lat_max))

But when using ScatterMapbox, there doesn’t appear to be a similar setting. The only thing that can be controlled is the center lat/long points and the zoom level. That would be fine, but it isn’t at all obvious how to set the zoom to achieve the same results as setting the lonaxis and lataxis as in the ScatterGeo.

Is there some other way to achieve this? While a user could control the zoom and the visible area in the interactive chart, I am trying to also generate a static PNG or PDF image so I need to be able to control the visible area.

Did you ever figure this out? I am trying to solve the same problem.

I sort of got something to work.

In my application I have a list of lat/long points. So I calculate the min and max lat and long and then set the zoom in the mapbox field of the layout using this function:

def getBoundsZoomLevel(bounds, mapDim):
    """
    source: https://stackoverflow.com/questions/6048975/google-maps-v3-how-to-calculate-the-zoom-level-for-a-given-bounds
    :param bounds: list of ne and sw lat/lon
    :param mapDim: dictionary with image size in pixels
    :return: zoom level to fit bounds in the visible area
    """
    ne_lat = bounds[0]
    ne_long = bounds[1]
    sw_lat = bounds[2]
    sw_long = bounds[3]

    scale = 2 # adjustment to reflect MapBox base tiles are 512x512 vs. Google's 256x256
    WORLD_DIM = {'height': 256 * scale, 'width': 256 * scale}
    ZOOM_MAX = 18

    def latRad(lat):
        sin = np.sin(lat * np.pi / 180)
        radX2 = np.log((1 + sin) / (1 - sin)) / 2
        return max(min(radX2, np.pi), -np.pi) / 2

    def zoom(mapPx, worldPx, fraction):
        return np.floor(np.log(mapPx / worldPx / fraction) / np.log(2))

    latFraction = (latRad(ne_lat) - latRad(sw_lat)) / np.pi

    lngDiff = ne_long - sw_long
    lngFraction = ((lngDiff + 360) if lngDiff < 0 else lngDiff) / 360

    latZoom = zoom(mapDim['height'], WORLD_DIM['height'], latFraction)
    lngZoom = zoom(mapDim['width'], WORLD_DIM['width'], lngFraction)

    return min(latZoom, lngZoom, ZOOM_MAX)

Interesting. Looks like it might be a bit jumpy but way better than going back to default pan/zoom. thanks for the update!