Mapbox-gl-native: Limit Viewport

Created on 19 Jan 2016  Â·  25Comments  Â·  Source: mapbox/mapbox-gl-native

I have been hearing about the feature that developers want to limit the viewport for the end-user.

Sample use-case:

Create a tourist application for NYC, users are restricted to:

  • move the map in NYC area
  • only allowed to zoom between 8 - 14 level
  • tilt is limited to only 20 degrees.
API:

This will result in exposing a similar API as the old raster API:

  • setScrollableAreaLimit() // definition ready
  • setMinZoom() // implemented
  • setMaxZoom() // implemented
  • setMinTilt()
  • setMaxTilt()

iOS is currently thinking about implementing https://github.com/mapbox/mapbox-gl-native/issues/2457, which will work similar to the rotation snap-back animation we currently have at very low zoom levels.

Android GL JS parity feature

All 25 comments

With the map tilted, does it matter that the user will be able to see beyond the scrollable area limit?

In other words, should these properties constrain the center point or the viewable bounds? If the latter, there could potentially be a complex interaction between the scrollable area limit, zoom, and tilt constraints.

2341 is one suggested approach, on iOS.

+1 on setScrollableAreaLimit
At least something like #2341

+1 on limiting scrollview

+1 limiting scrollview

@tobrun Let's get this into 4.0.0. Cool?

@bleege :+1:

@zugaldia
any preference on how we are going to implement this?
This seems to be an overlapping requirement with offline?

@zugaldia any preference on how we are going to implement this? This seems to be an overlapping requirement with offline?

I'm not sure there's an overlap. Offline tiles would be handled by app users, while a limited viewport would be set by the app developer, isn't it?

That is true, but how are you restricting bounds there? eg, user downloads region of NYC and than tries to pan to New Jersey?

How about this solution? #2341
Seems to solve the issue, disregarding some factors, but works good enough.
Then extend the solution to cover rotation/tilt/and non-rectangular shapes.
At least there is a solution that covers one very common use case.

+1 on setScrollableAreaLimit
both in offline and online mode :)

Noticed some functionality is implemented and some still needs to work on

Do we know if there is a plan to add this feature request to Milestone ?

@guruduttstay As discussed in https://github.com/mapbox/mapbox-gl-native/issues/5238#issuecomment-228077268 we probably won't add any new features to 4.1.0 but this is something we are still considering for future releases.

+1 on setScrollableAreaLimit

@tobrun would enabling developers to change the native map's current ConstrainMode be something that's in scope of this issue or would that be deserving of another ticket?

@tobrun bump... sorry :\

If the ConstrainMode::ConstrainWidthAndHeight does what I think it does (prevents world wrapping) then it would likely be a solution to the problem I'm trying to solve

@sddamico sorry for getting back to you just now. I'm actually surprised about that class. I'm looking into adding it to the Android binding to see what it actually does and solves the use-case mentioned above.

Yes, ConstrainMode can be used to prevent the user from panning across the antimeridian. This ticket is more generally about constraining the viewport to an arbitrary bounding box or shape.

@1ec5 thank you for adding that. I have been looking at how gl-js has implemented this.
It seems they expose a setMaxBounds. This is used by the _constrain function which is called whenever the camera changes.

Would it be feasible to port over this logic to gl-native?

The GL JS implementation allows the developer to constrain the viewable area to a rectangular bounding box, without accounting for rotation or pitch. (If I’m reading the code correctly, the user can still peek beyond the viewable area by rotating or tilting the map.) This approach implements a hard limit, so it won’t accommodate any “rubber band” effect we might want to introduce later (along the lines of #2191).

If the GL JS approach is sufficient for the Android SDK despite these caveats, it might be easier to write the logic from scratch than to port the GL JS logic. You’ll want to plumb the options down into TransformState::constrain() and use logic similar to what we already do for ConstrainMode. While you’re at it, you might want to generalize ConstrainMode to be just a special case of setting the maximum bounds.

For the iOS and macOS SDKs, I’m still committed to a different approach outlined in #5584, which allows for a non-rectangular viewable area and would allow for a “rubber band” effect in the future. So those SDKs wouldn’t be using a bbox-style constraint option.

I did a quick method by utilising the mapboxMap OnScrollListener and OnFlingListener together with the camera method demonstrated here:

https://www.mapbox.com/android-sdk/examples/fit-bound-camera

Not perfect but kind of did the job, waiting for a more stable and official method from the SDK
I paste my code snippet here in case anyone is interested

public static void easeCameraBackToBoundingBox() {
        LatLngBounds latLngBounds = new LatLngBounds.Builder()
                .include(NE_LIMIT) // Northeast
                .include(SW_LIMIT) // Southwest
                .build();

        mapboxMap.easeCamera(CameraUpdateFactory.newLatLngBounds(latLngBounds, 0), 100);
    }

Below is the method i place in my OnScrollListener and OnFlingListener:

public static void restrictMapToBoundingBox() {
        VisibleRegion visibleRegion = mapboxMap.getProjection().getVisibleRegion();

        Double maxLat = NE_LIMIT.getLatitude();
        Double maxLng = NE_LIMIT.getLongitude();
        Double minLat = SW_LIMIT.getLatitude();
        Double minLng = SW_LIMIT.getLongitude();

        if( !(visibleRegion.farLeft.getLatitude() >= minLat && visibleRegion.farLeft.getLatitude() <= maxLat
                && visibleRegion.farLeft.getLongitude() >= minLng && visibleRegion.farLeft.getLongitude() <= maxLng) ) {
           easeCameraBackToBoundingBox();
        }
        if( !(visibleRegion.farRight.getLatitude() >= minLat && visibleRegion.farRight.getLatitude() <= maxLat
                && visibleRegion.farRight.getLongitude() >= minLng && visibleRegion.farRight.getLongitude() <= maxLng) ) {
           easeCameraBackToBoundingBox();
        }
        if( !(visibleRegion.nearLeft.getLatitude() >= minLat && visibleRegion.nearLeft.getLatitude() <= maxLat
                && visibleRegion.nearLeft.getLongitude() >= minLng && visibleRegion.nearLeft.getLongitude() <= maxLng) ) {
           easeCameraBackToBoundingBox();
        }
        if( !(visibleRegion.nearRight.getLatitude() >= minLat && visibleRegion.nearRight.getLatitude() <= maxLat
                && visibleRegion.nearRight.getLongitude() >= minLng && visibleRegion.nearRight.getLongitude() <= maxLng) ) {
           easeCameraBackToBoundingBox();
        }
    }

Thanks @jlormee for sharing!

5584 landed for iOS SDK v3.5.0 and macOS SDK v0.4.0. Removing the iOS label.

Implementing core functionality in #8583.

8583 is the most straightforward approach to allowing the developer to limit the viewport, but I don’t expect that the iOS and macOS SDKs will need to adopt it. Instead, those SDKs persued a very different solution for the use case of limiting the viewport, as proposed in #2457: instead of fixed bounds and zoom levels, there’s a delegate method (callback) that allows the developer to decide on demand, in advance, whether to allow a change to the viewport.

The delegate approach is more consistent with standard scroll view APIs on the iOS and macOS platforms. It allows the developer to constrain the map to a non-polygonal region of the world and a non-rectangular-prism camera space, and it’ll eventually allow the iOS and macOS SDKs to implement rubber banding (#2191), varying the maximum pitch by zoom level (#6908), and snapping the rotation without special cases all over the gesture handling code. At a high level, this approach is consistent with a general push to complement declarative APIs with imperative APIs (other examples being runtime styling, computed GeoJSON sources, etc.).

With the delegate method already in place in #5584 and further conveniences coming in #8499, I don’t think it’ll be necessary to introduce another public API for limiting the viewport on iOS or macOS. However, if the Android SDK or other frontends need the more straightforward approach in #8583, it seems perfectly reasonable to provide this support at the core level.

Was this page helpful?
0 / 5 - 0 ratings