Create a `getMinZoomForBounds` method on the `Map` object
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 12.4k
- Forks
- 2.4k
- PR merge metrics
- No merged PRs in 30d
Description
## Motivation
I have a "zoom out" button that I would like to disable if the map can't be zoomed out any further. This happens in one of two cases:
- the zoom matches the map's `minZoom`
- the zoom matches the minimum zoom for the current `maxBounds`
Unfortunately, a naive `map.getZoom() == map.getMinZoom()` doesn't cut it, given that the method `map.getMinZoom()` [returns the configured `minZoom`](https://github.com/mapbox/mapbox-gl-js/blob/2b2743a69bfbfeb5745180524d9322b554398068/src/ui/map.ts#L1096), without checking the `maxBounds` at all.
For context, the built-in navigation control uses [the "naive" condition](https://github.com/mapbox/mapbox-gl-js/blob/2b2743a69bfbfeb5745180524d9322b554398068/src/ui/control/navigation_control.ts#L93). This effectively leaves the zoom out button enabled in all those scenarios where the `maxBounds` lead to a minimum allowed zoom that is more restrictive than the map's min zoom.
## Design Alternatives
Doing nothing is an option. However, to achieve the desired behaviour, developers are forced to repeat logic that is already present internally. My user-land implementation closely follows what's available in the `Transform` class, [in the `_minZoomForBounds` method](https://github.com/mapbox/mapbox-gl-js/blob/2b2743a69bfbfeb5745180524d9322b554398068/src/geo/transform.ts#L2213):
```ts
protected onMapZoomEnd({ target }: MapEventOf<'zoomend'>) {
const transform = target.transform;
let minZoom = Math.max(0, transform.scaleZoom(transform.height / (transform.worldMaxY - transform.worldMinY)));
minZoom = Math.max(minZoom, transform.scaleZoom(transform.width / (transform.worldMaxX - transform.worldMinX)));
this.isMax.set(target.getMaxZoom() == target.getZoom());
// For unknown reasons, sometimes the fully-zoomed out view and the minZoom won't match
// When that happens, the difference is usually around 8e-16
this.isMin.set(Math.abs(minZoom - target.getZoom()) < Number.EPSILON * 10);
}
```
## Design
A new `map.getMinZoomForBounds()` method. A new method would have the advantage of letting existing code that relies on the current behaviour of `getMinZoom` stay unchanged. In a library this popular, I'd argue this is quite important.
### Mock-Up
The updated code to disable the zoom out button could look like this (React pseudo-code):
```tsx
// Instead of
// let minZoom = Math.max(0, map.transform.scaleZoom(map.transform.height / (map.transform.worldMaxY - map.transform.worldMinY)));
// minZoom = Math.max(minZoom, map.transform.scaleZoom(map.transform.width / (map.transform.worldMaxX - map.transform.worldMinX)));
// const isDisabled = map.getZoom() == minZoom;
// Do
const isDisabled = map.getZoom() == map.getMinZoomForBounds();
return Zoom out;
```
### Concepts
I don't know of any precedents, although the fact that it's an internal function, used by the library to make decisions ("can I zoom out?") that one can't easily probe from the outside, should be plenty of reasons to expose this information.
### Implementation
The implementation would be quite straightforward:
```ts
// mapbox-gl-js/src/ui/map.ts
getMinZoomForBounds(): number {
return this.transform._minZoomForBounds(); // or better: rename to "minZoomForBounds" (drop the _)
}
```
The only quirk I've identified in my user-land code is that, when the user is fully zoomed out, the `minZoomForBounds` doesn't always match the current zoom. The difference is minimal (`±8e-16`), but it's still 5x the size of `Number.EPSILON`, so it can't be attributable to loss of precision, as far as I know. That should probably be a separate issue, though.
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
Start in src/ui/map.ts by reviewing getMinZoom and the proposed getMinZoomForBounds entry point, then read src/geo/transform.ts and its _minZoomForBounds method. Confirm how maxBounds affect the minimum zoom and preserve the existing getMinZoom behavior. Done means the Map API exposes the bounds-aware value for callers such as the navigation control.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- frontend
- Issue type
- Feature
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Stale
- Clarity
- Clearly specified
- Newbie friendliness
- 48/100