Skip to content
This repository has been archived by the owner on Aug 8, 2023. It is now read-only.

Limit Viewport #3602

Closed
tobrun opened this issue Jan 19, 2016 · 25 comments · Fixed by #8622
Closed

Limit Viewport #3602

tobrun opened this issue Jan 19, 2016 · 25 comments · Fixed by #8622
Assignees
Labels
Android Mapbox Maps SDK for Android feature GL JS parity For feature parity with Mapbox GL JS

Comments

@tobrun
Copy link
Member

tobrun commented Jan 19, 2016

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 #2457, which will work similar to the rotation snap-back animation we currently have at very low zoom levels.

@tobrun tobrun added iOS Mapbox Maps SDK for iOS Android Mapbox Maps SDK for Android feature request labels Jan 19, 2016
@1ec5
Copy link
Contributor

1ec5 commented Jan 19, 2016

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

@1ec5
Copy link
Contributor

1ec5 commented Jan 21, 2016

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.

@friedbunny
Copy link
Contributor

#2341 is one suggested approach, on iOS.

@Zakay
Copy link

Zakay commented Feb 10, 2016

+1 on setScrollableAreaLimit
At least something like #2341

@daklock
Copy link

daklock commented Feb 11, 2016

+1 on limiting scrollview

@s-a-y
Copy link

s-a-y commented Feb 15, 2016

+1 limiting scrollview

@bleege
Copy link
Contributor

bleege commented Feb 17, 2016

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

@bleege bleege added this to the android-v4.0.0 milestone Feb 17, 2016
@tobrun
Copy link
Member Author

tobrun commented Feb 18, 2016

@bleege 👍

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

@zugaldia
Copy link
Member

@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?

@tobrun
Copy link
Member Author

tobrun commented Feb 18, 2016

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

@Zakay
Copy link

Zakay commented Feb 25, 2016

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.

@guruduttstay
Copy link

guruduttstay commented Jun 23, 2016

+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 ?

@zugaldia
Copy link
Member

@guruduttstay As discussed in #5238 (comment) we probably won't add any new features to 4.1.0 but this is something we are still considering for future releases.

@Nucifera8472
Copy link

+1 on setScrollableAreaLimit

@sddamico
Copy link

@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?

@sddamico
Copy link

@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

@tobrun
Copy link
Member Author

tobrun commented Sep 6, 2016

@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.

@1ec5
Copy link
Contributor

1ec5 commented Sep 6, 2016

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.

@tobrun
Copy link
Member Author

tobrun commented Sep 6, 2016

@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?

@1ec5
Copy link
Contributor

1ec5 commented Sep 6, 2016

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.

@cammace cammace added this to the android-future milestone Oct 12, 2016
@jlormee
Copy link

jlormee commented Nov 15, 2016

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();
        }
    }

@zugaldia
Copy link
Member

Thanks @jlormee for sharing!

@1ec5
Copy link
Contributor

1ec5 commented Mar 6, 2017

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

@1ec5 1ec5 added GL JS parity For feature parity with Mapbox GL JS Google Maps parity For feature parity with the Google Maps SDK for Android or iOS and removed iOS Mapbox Maps SDK for iOS Google Maps parity For feature parity with the Google Maps SDK for Android or iOS labels Mar 6, 2017
@brunoabinader brunoabinader self-assigned this Mar 30, 2017
@brunoabinader
Copy link
Member

Implementing core functionality in #8583.

@1ec5
Copy link
Contributor

1ec5 commented Mar 30, 2017

#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.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Android Mapbox Maps SDK for Android feature GL JS parity For feature parity with Mapbox GL JS
Projects
None yet
Development

Successfully merging a pull request may close this issue.