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

Add pitch argument to cameraThatFits functions #12213

Merged
merged 18 commits into from
Jul 2, 2018
Merged

Conversation

bsudekum
Copy link

@bsudekum bsudekum commented Jun 25, 2018

In the navigation sdk, we use MGLMapView.cameraThatFitsShape(_:direction: edgePadding) to go from navigation mode to overhead mode. However, since this function does not provide a pitch argument, the function relies on the mapview's current pitch to calculate the new camera. This does not work well because we want to end up overhead, looking straight down.

Because of this, we have to jarringly set the pitch to 0, then find the appropriate camera that fits the current route line. If we could pass in the desired pitch, this first camera animation would not be required.

This change allows for developers to pass in a desired pitch for a few of the cameraThatFites... functions.

/cc @asheemmamoowala @1ec5 @frederoni

todo:

  • add documentation

@bsudekum bsudekum requested review from 1ec5 and asheemmamoowala June 25, 2018 19:48
@bsudekum bsudekum added iOS Mapbox Maps SDK for iOS navigation For the Mapbox Navigation SDK for Android or iOS or navigation use cases in general labels Jun 25, 2018
CameraOptions Map::cameraForLatLngs(const std::vector<LatLng>& latLngs, const EdgeInsets& padding, optional<double> bearing) const {
if(bearing) {
CameraOptions Map::cameraForLatLngs(const std::vector<LatLng>& latLngs, const EdgeInsets& padding, optional<double> bearing, optional<double> pitch) const {
if (bearing && pitch) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will provide incorrect results if only one of bearing or pitch is provided. The if clause would need to handle each of bearing and pitch individually.

options.angle = transform.getAngle();
}
if (pitch) {
options.angle = transform.getAngle();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should set pitch here.


CameraOptions options = mbgl::cameraForLatLngs(latLngs, transform, padding);

if (bearing) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Angle and pitch can be set on options without the additional if blocks.

@@ -84,7 +84,7 @@ TEST(Map, LatLngBoundsToCameraWithAngle) {
CameraOptions virtualCamera = test.map.cameraForLatLngBounds(bounds, {}, 35);
ASSERT_TRUE(bounds.contains(*virtualCamera.center));
EXPECT_NEAR(*virtualCamera.zoom, 1.21385, 1e-5);
EXPECT_DOUBLE_EQ(virtualCamera.angle.value_or(0), -35 * util::DEG2RAD);
EXPECT_NEAR(virtualCamera.angle.value_or(0), -35 * util::DEG2RAD, 1e-6);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

May need this on Line 96 below as well. While you're at it, could you also add a test with pitch and bearing, and oe with just pitch.

Bobby Sudekum added 2 commits June 27, 2018 11:08
transform.setAngle(angle);
}
if (pitch) {
double pitchAsRadian = *pitch * util::DEG2RAD; // Convert to radians
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unlike bearing, negative does not work here. Does this look right to you @asheemmamoowala?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes thats correct. Bearing is counter clockwise from North,

Copy link
Contributor

@1ec5 1ec5 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR implements #12238. Please make any corresponding changes to the macOS implementation of MGLMapView, then add a blurb to the iOS and macOS map SDKs noting the addition of these methods.

@@ -926,6 +926,23 @@ MGL_EXPORT IB_DESIGNABLE
*/
- (MGLMapCamera *)cameraThatFitsCoordinateBounds:(MGLCoordinateBounds)bounds edgePadding:(UIEdgeInsets)insets;

/**
Returns the camera that best fits the given coordinate bounds, optionally with
some additional padding on each side.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This sentence should indicate that the method lets the developer specify a direction and pitch.

@return A camera object centered on the shape's center with zoom level as high
(close to the ground) as possible while still including the entire shape.
*/
- (MGLMapCamera *)cameraThatFitsShape:(MGLShape *)shape direction:(CLLocationDirection)direction pitch:(CGFloat)pitch edgePadding:(UIEdgeInsets)insets;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are some problems with the design of this API:

  • This represents the first time we’re exposing pitch as an option in MGLMapView as opposed to MGLMapCamera. Up to now, we’ve deliberately excluded pitch parameters from MGLMapView, because they make more sense in MGLMapCamera’s 3D context than in MGLMapView’s largely 2D context. For example, MGLMapCamera offers two frames of reference (viewer and focal point), whereas MGLMapView can only express the focal point.
  • We’re trying to limit the number of viewport-related methods in MGLMapView, to avoid combinatorial explosion. What if the developer wants to specify a direction but pitch but not a direction?
  • From the method name (and even from the documentation), the relationship between the shape and the direction is unclear, and so is the relationship between the shape and the pitch.

+[MGLMapCamera cameraLookingAtCenterCoordinate:fromDistance:pitch:heading:] does a better job of describing the relationship between the center coordinate and the other parameters. Can we do something similar for shapes and coordinate bounds? Maybe something that explicitly returns a copy of an MGLMapCamera, like -[MGLMapView camera:fittingShape:edgePadding:]? We could deprecate -[MGLMapView cameraThatFitsShape:direction:edgePadding:] at the same time, since MGLMapCamera has a convention where values like −1 are ignored.

mbgl::EdgeInsets padding = MGLEdgeInsetsFromNSEdgeInsets(insets);
padding += MGLEdgeInsetsFromNSEdgeInsets(self.contentInset);
mbgl::CameraOptions cameraOptions = _mbglMap->cameraForLatLngBounds(MGLLatLngBoundsFromCoordinateBounds(bounds), padding, direction, pitch);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does mbgl::Map::cameraForLatLngBounds() correctly handle the situation where either direction or pitch is negative? A negative value in an MGLMapCamera property normally means that the property is ignored.

if (camera.heading >= 0)
{
options.angle = MGLRadiansFromDegrees(-camera.heading);
}
if (camera.pitch >= 0)
{
options.pitch = MGLRadiansFromDegrees(camera.pitch);
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All the mbgl::Map::cameraFor... methods end up in mbgl::Map::cameraForLatLngs. The input values are wrapped and clamped appropriately before use.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Before clamping, we need to check for < 0, which means “ignore” only on iOS and macOS, but not on other platforms.

Bobby Sudekum added 2 commits June 28, 2018 14:25
@bsudekum
Copy link
Author

@1ec5 updated and changed this PR to add a single new function, -[MGLMapView camera:fittingShape:edgePadding:]

`0` results in a camera pointed straight down at the map. Angles greater
than `0` result in a camera angled toward the horizon.
@param camera The camera that the return camera should adhere to. All values
on this camera will be manipulated except for pitch and direction
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: missing a period at the end.

return [self cameraForCameraOptions:cameraOptions];

CGFloat pitch = camera.pitch;
CLLocationDirection direction = camera.heading;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When pitch or heading is negative, it should be ignored in favor of the most appropriate value (which can be the current value).

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm this brings up a good point. I feel like the lowest level function should be responsible for normalizing the direction.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the lowest-level method at which the normalization is relevant. The negative-means-ignored behavior is specific to Apple platforms.

*/
- (MGLMapCamera *)cameraThatFitsCoordinateBounds:(MGLCoordinateBounds)bounds direction:(CLLocationDirection)direction pitch:(CGFloat)pitch edgePadding:(UIEdgeInsets)insets;
- (MGLMapCamera *)camera:(MGLMapCamera *)camera fittingShape:(MGLShape *)shape edgePadding:(UIEdgeInsets)insets;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a need for a -camera:fittingCoordinateBounds:edgePadding: as well?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, will add.

@1ec5
Copy link
Contributor

1ec5 commented Jun 29, 2018

Also, remember to make any corresponding changes to the macOS implementation of MGLMapView, then add a blurb to the iOS and macOS map SDKs noting the addition of these methods.

@@ -2,6 +2,10 @@

Mapbox welcomes participation and contributions from everyone. Please read [CONTRIBUTING.md](../../CONTRIBUTING.md) to get started.

# master

* Added `-[MGLMapView camera:fittingShape:edgePadding:]` and `-[MGLMapView camera:fittingCoordinateBounds:edgePadding:]` allowing you specify the pitch and direction for the calculated camera.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also add a link to this PR in parentheses.

return [self cameraForCameraOptions:cameraOptions];

CGFloat pitch = camera.pitch;
CLLocationDirection direction = camera.heading;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the lowest-level method at which the normalization is relevant. The negative-means-ignored behavior is specific to Apple platforms.

Copy link
Contributor

@1ec5 1ec5 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One minor change to make, but otherwise good to go.

padding += MGLEdgeInsetsFromNSEdgeInsets(self.contentInsets);

CGFloat pitch = camera.pitch < 0 ? _mbglMap->getPitch() : camera.pitch;
CLLocationDirection direction = camera.heading < 0 ? _mbglMap->getBearing() : camera.heading;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When possible, use existing property getters to ensure that normalization is applied consistently:

MGLMapCamera *currentCamera = self.camera;
CGFloat pitch = camera.pitch < 0 ? currentCamera.pitch : camera.pitch;
CLLocationDirection direction = camera.heading < 0 ? currentCamera.heading : camera.heading;

(There’s also an MGLMapView.direction, but we have to call -camera anyways to get the current pitch.)

×4

@bsudekum bsudekum merged commit 4286d58 into master Jul 2, 2018
@bsudekum bsudekum deleted the cameraThatFitsWithPitch branch July 2, 2018 23:30
@1ec5 1ec5 added the macOS Mapbox Maps SDK for macOS label Jul 12, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
iOS Mapbox Maps SDK for iOS macOS Mapbox Maps SDK for macOS navigation For the Mapbox Navigation SDK for Android or iOS or navigation use cases in general
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants