Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature] Allow "underzooming" to show entire map on non-square viewports when renderWorldCopies=false #4510

Open
larsmaxfield opened this issue Aug 5, 2024 · 21 comments · May be fixed by #4733

Comments

@larsmaxfield
Copy link
Contributor

larsmaxfield commented Aug 5, 2024

Currently if renderWorldCopies = false and the viewport is not square, the map does not allow the user to zoom out enough to show the entire map.

You can see this in the render world copies example by fully zooming out and toggling the option:

current-with-copies

current-no-copies

Zooming out is limited to when the map's bounds are reached on the left and right sides (with a wide viewport) or the top and bottom sides (with a tall viewport). I assume this is the intended behavior. Edit: This appears to not be the intended behavior; see comment below.

However, this can be abrupt for users who expect to be able to see the entire map when they attempt to fully zoom out the map.

I would like an option to allow the map to be "underzoomed" until the entire map is visible when renderWorldCopies = false, whereby the background area outside the map is transparent or assigned a color:

desired

Perhaps the option could be something like showEntireMap or allowUnderZoom, though I'm not familiar with best practices in naming and implementation.

(One could think of this as (un)constraining the zoom and pan of the map, which the OpenSeadragon image viewer does nicely here: https://openseadragon.github.io/examples/ui-zoom-and-pan/.)

@larsmaxfield
Copy link
Contributor Author

The currently restricted zoom-out behavior is not intended if I understand the renderWorldCopies docs correctly:

When the map is zoomed out far enough that a single representation of the world does not fill the map's entire container, there will be blank space beyond 180 and -180 degrees longitude.

@sbachinin
Copy link
Collaborator

Restricted behaviour is intended, despite this quote.
Making it non-restricted looked a bit problematic. Also I assumed that viewing the whole globe is an unlikely demand.
Could you please describe the use case where it is necessary?

@larsmaxfield
Copy link
Contributor Author

larsmaxfield commented Aug 5, 2024

I was able to implement this by modifying a few lines of getConstrained() in transform.ts. Works nice on a 2D map:

under-zooming-2d

(Edit: See below comment for a temporary workaround for 3D.) However, on a 3D terrain map, it locks up when under-zoomed below 0 or when pitching at zoom levels below 2, sometimes throwing an 'outside of bounds' error:

tile_id.ts:21  Uncaught Error: x=0, y=2, z=0.27 outside of bounds. 0<=x<1.2058078276907604, 0<=y<1.2058078276907604 0<=z<=25 
    at CanonicalTileID (tile_id.ts:21:19)
    at new OverscaledTileID (tile_id.ts:96:26)
    at Terrain._getOverscaledTileIDFromLngLatZoom (terrain.ts:455:24)
    at Terrain.getElevationForLngLatZoom (terrain.ts:187:53)
    at Map._elevateCameraIfInsideTerrain (camera.ts:1122:42)
    at camera.ts:1144:39
    at Map._applyUpdatedTransform (camera.ts:1161:17)
    at HandlerManager._fireEvents (handler_manager.ts:594:23)
    at HandlerManager.stop (handler_manager.ts:309:14)
    at HandlerManager.handleEvent (handler_manager.ts:359:18)

@larsmaxfield
Copy link
Contributor Author

larsmaxfield commented Aug 5, 2024

Restricted behaviour is intended, despite this quote. Making it non-restricted looked a bit problematic. Also I assumed that viewing the whole globe is an unlikely demand. Could you please describe the use case where it is necessary?

Hi @sbachinin, the general use case is for when users wish to see the entire map no matter the aspect ratio of the viewport. For me specifically, I am implementing MapLibre for visualizing scans of paintings as single maps, and users have requested the ability to "fully zoom out" where they expect to see the entire painting.

Current behavior with zooming out limited:
painting-no-underzoom

Desired behavior with under-zooming allowed:
painting-underzoom

@larsmaxfield
Copy link
Contributor Author

Disabling the bounds error in the constructor for CanonicalTileID (tile_id.ts) allows 3D to work with under-zooming:

under-zooming-3d

@HarelM
Copy link
Collaborator

HarelM commented Aug 5, 2024

I would say that for paintings you can have them shown in a larger zoom, can't you? i.e. show full painting in zoom 3, or even 7 would solve this, wouldn't it?

@larsmaxfield
Copy link
Contributor Author

@HarelM ah, do you mean by padding the original images with a wide border so that the generated tilesets have plenty of empty space around the actual scan?

@sbachinin
Copy link
Collaborator

Not sure if it's the same as what Harel means but -
if you don't stretch the paintings to the whole globe but render them somewhat smaller in the center of the globe, it must be better in every way. E.g., the paintings' aspect ratio can be any.
I honestly don't know how to implement this but it must be doable

@larsmaxfield
Copy link
Contributor Author

larsmaxfield commented Aug 6, 2024

I use pyvips dzsave which tiles the painting by scaling the original image by half until it fits within a single tile, which then becomes the lowest zoom 0/0/0.jpg tile. The z-depth then depends on how many times the image was halved, or rather how large the original image was.

I hadn't yet considered rendering it smaller than the map. Perhaps because of how pyvips does the tiling. For me it makes more sense that the painting always fills the 0/0/0 tile. I don't need to keep track of minimum zoom or bounds.

I have considered centering the painting, but for coordinate transformation that adds one more step (an XY offset). Having (0,0) as the origin for everything is easier.

@HarelM
Copy link
Collaborator

HarelM commented Aug 6, 2024

You can use image source instead of tiling I believe.

@larsmaxfield
Copy link
Contributor Author

The scans are of gigapixel magnitude — 100k × 100k px — so we rely on tiling.

@sbachinin
Copy link
Collaborator

In my opinion, the idea of "underzooming" (viewing the whole globe on a single-globe map) totally makes sense.
It feels better to be able to zoom out enough to view the whole thing.
So if this can be implemented without much complexity, why not?
I also think that this can be the default behavior of a single-globe map, that is, I wouldn't introduce this new option because it looks a little odd.
I don't think that anyone is going to suffer from having blank margins around the globe.

As for the complexity, I doubt that you can make it work well with little code.
E.g., I think it should behave like this example from OpenSeadragon. That is, panning should still have some constraints (it must be impossible to outpan the map from the viewport completely). This is perhaps not a trivial task.
Also there is a lot of stuff that (potentially) can be broken by this change, like markers and popups.

@larsmaxfield
Copy link
Contributor Author

Good points. I agree that this should become the new default behavior for single-globe maps.

I'll give it a try and update here when I've got something working.

@larsmaxfield
Copy link
Contributor Author

larsmaxfield commented Aug 27, 2024

I've proposed a solution in PR #4612.

I've rescinded that solution as I've found a more intuitive design with two user settings — one for underzooming and one for overpanning. I'll explain here once I have a demo.

@larsmaxfield
Copy link
Contributor Author

larsmaxfield commented Sep 20, 2024

Here is a demo of my new design that allows you to underzoom and overpan a single-copy world:

https://larsmaxfield.github.io/swings/maplibre-underzoom/unbounded.html
Underzoom demo of unbounded map.

There are three parameters which I currently name as follows:

  • allowUnderzoom: Boolean that enables/disables underzooming and overpanning.

  • underzoom: Number 0–100 that defines how far you can underzoom the map bounds as a percent of the map's size relative to the viewport. If 100, you can underzoom the map until it is 100% the size of the viewport. If 50, the map can be underzoomed until it is 50% the size of the viewport.

  • overpan: Number 0-50 that defines how far you can overpan the latitude-longitude bounds of the map as a percent of the viewport height and width. If 50, you can overpan the map until the latitude-longitude bounds reach the viewport center (half the height and width). If 25, you can overpan the map until the bounds reach 25% the viewport height and width. If 0, you cannot overpan the bounds.

The minZoom is always respected, meaning underzoom = 0 will allow you to underzoom until minZoom is reached.

If you underzoom the map such that the bounds must exceed the limit of overpan, the map will allow that. In other words, overpan is overridden by underzoom when necessary. (Try setting overpan = 0 and see that zooming out effectively centers the underzoomed map.)

Here are demos on tall- and wide-bounded maps:

Tall area:
https://larsmaxfield.github.io/swings/maplibre-underzoom/tall-bounds.html
Underzoom demo of a tall map.

Wide area:
https://larsmaxfield.github.io/swings/maplibre-underzoom/wide-bounds.html
Underzoom demo of a tall map.

@HarelM
Copy link
Collaborator

HarelM commented Sep 20, 2024

Why not use minZoom and bounds to define this behavior instead of introducing new configurations?

@larsmaxfield
Copy link
Contributor Author

larsmaxfield commented Sep 20, 2024

Why not use minZoom and bounds to define this behavior instead of introducing new configurations?

I considered that, but:

minZoom is based on absolute tile size, not the relative viewport size. This means that if I wanted to have a consistent underzooming size (say, allow a user to underzoom the map to 50% the viewport size), I would need to monitor the viewport size and update minZoom every time it changes.

bounds cannot be set beyond the built-in lnglat maximums. This makes it impossible to underzoom a map on a tall viewport.

@HarelM
Copy link
Collaborator

HarelM commented Sep 20, 2024

I tend to think that the definitions added here are not in line with the previous configurations.
I would consider thinking which APIs this features needs and write it down as a plugin...

@larsmaxfield
Copy link
Contributor Author

larsmaxfield commented Sep 20, 2024

I tend to think that the definitions added here are not in line with the previous configurations.
I would consider thinking which APIs this features needs and write it down as a plugin...

Could you explain how they're not in line? Or point to a good example I could learn from?

If it helps, the approach I took here is to default the map to current MapLibre behavior — no underzoom and no overpan unless both renderWorldCopies=false and allowUnderzoom=true. Also, the only function my implementation significantly adds to is Tranform.getConstrained().

Good advice to see how a plugin could solve this. I'm still getting familiar with everything that can be done with MapLibre and hadn't considered that. I will try that out.

Nevertheless I agree with @sbachinin that underzooming could be the default single-world behavior.

@HarelM
Copy link
Collaborator

HarelM commented Sep 20, 2024

Most options are not in the range of 0 to 1, see here:
https://maplibre.org/maplibre-gl-js/docs/API/type-aliases/MapOptions/
I don't have a better suggestion though, unfortunately...

@larsmaxfield larsmaxfield linked a pull request Sep 21, 2024 that will close this issue
8 tasks
@larsmaxfield
Copy link
Contributor Author

I updated the number options to be percentages with underzoom in the range 0–100 and overpan in the range 0–50 (see edited explanation above). I also made a draft PR to check the impact of the design.

Still looking into the plugin route.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
3 participants