mapbox / mapbox-maps-ios

Interactive, thoroughly customizable maps for iOS powered by vector tiles and Metal
https://www.mapbox.com/mapbox-mobile-sdk
Other
462 stars 149 forks source link

Setting camera bounds with padding fails [v11] #2134

Closed naftalibeder closed 2 weeks ago

naftalibeder commented 6 months ago

Environment

Observed behavior and steps to reproduce

When setting the camera to a coordinate bounds with padding, the camera does not zoom out to encompass the provided bounds.

To reproduce, run this example with Mapbox >= 11.0.0 and click the Change button repeatedly:

CameraAnimationExample.swift

```swift import UIKit import MapboxMaps final class CameraAnimationExample: UIViewController, ExampleProtocol { private var mapView: MapView! private var cameraOptions: CameraOptions! private var flag = 0 override func viewDidLoad() { super.viewDidLoad() mapView = MapView(frame: view.bounds) mapView.ornaments.scaleBarView.isHidden = true try? mapView.mapboxMap.setProjection(.init(name: .mercator)) mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] view.addSubview(mapView) let button = UIButton(type: .roundedRect) button.backgroundColor = .white button.setTitle("Change", for: .normal) button.layer.position = .init(x: 100, y: 100) button.frame.size = .init(width: 200, height: 40) button.addTarget(self, action: #selector(onClick), for: .touchUpInside) view.addSubview(button) mapView.mapboxMap.onMapLoaded.observeNext { _ in self.onClick() } } @objc func onClick() { switch flag { case 0: let padding = UIEdgeInsets( top: 0, left: 0, bottom: 500, right: 0 ) cameraOptions = CameraOptions( center: CLLocationCoordinate2D( latitude: 41.888579, longitude: -87.804039 ), padding: padding, zoom: 14, bearing: 0, pitch: 0 ) print("Camera 1 | Padding \(padding.top + padding.bottom), map height \(mapView.bounds.height)") case 1: let padding = UIEdgeInsets( top: 0, left: 0, bottom: 500, right: 0 ) cameraOptions = mapView.mapboxMap.camera( for: CoordinateBounds( southwest: CLLocationCoordinate2D( latitude: 41.87726211032818, longitude: -87.80420963106079 ), northeast: CLLocationCoordinate2D( latitude: 41.880369537640505, longitude: -87.80199949078224 ) ), padding: padding, bearing: nil, pitch: nil, maxZoom: nil, offset: nil ) print("Camera 2 | Padding \(padding.top + padding.bottom), map height \(mapView.bounds.height)") default: break } mapView.camera.ease(to: cameraOptions, duration: 1) flag += 1 if flag == 2 { flag = 0 } } } ```

Expected behavior

On repeat click, the camera should alternatively ease between a center coordinate and a slightly zoomed out bounds, both bottom-padded.

Actual behavior

The camera moves laterally to the approximate bounding box, but the zoom does not change. The following seemingly contradictory data prints to the console:

Camera 1 | Padding 500.0, map height 852.0 Camera 2 | Padding 500.0, map height 852.0 [Warning, maps-core]: {}[General]: Unable to calculate camera for given bounds/geometry, padding is greater than map's width or height.

Notes / preliminary analysis

Additional links and references

The above example behaves correctly on v10:

https://github.com/mapbox/mapbox-maps-ios/assets/2423291/d2e935cd-d6f5-4ee4-952f-b00a0858f08f

but fails on v11:

https://github.com/mapbox/mapbox-maps-ios/assets/2423291/4ca88720-6bae-4ca4-8ad5-7f824ed2fa57

aleksproger commented 6 months ago

@naftalibeder this should be fixed in 11.2 may you please verify and close the issue if it was resolved

aleksproger commented 6 months ago

Also please migrate to use the following method https://docs.mapbox.com/ios/maps/api/11.2.0/documentation/mapboxmaps/mapboxmap/camera(for:camera:coordinatespadding:maxzoom:offset:)/ As we will deprecate other camera(for:) methods soon

amzada commented 2 months ago

hi @aleksproger I'm struggling slightly with this one myself. I understand the camera(for:) methods will be deprecated soon however I'm not quite sure how I can use https://docs.mapbox.com/ios/maps/api/11.2.0/documentation/mapboxmaps/mapboxmap/camera(for:camera:coordinatespadding:maxzoom:offset:)/ to achieve the same result as the method that takes in a rect. Let's say I'd like to draw a route on the map screen. The route could be drawn vertically or horizontally across the map/screen. However I'd like to display the full route on the top half of the map screen. How can I make sure that the route (regardless of it being vertically or horizontally across the screen) would always be displayed in the top half of the screen? I'm guessing I need to do a combination of coordinates padding + offset but haven't figured out how to best do it?

aleksproger commented 2 months ago

Hi, @amzada if I understand correctly you may do smth like this

let coordinateBounds = mapView.mapboxMap.coordinateBounds(for: CGRect())
let boundingPolygonCameraOptions = try mapView.mapboxMap.camera(
    for: [cooridnateBounds.northwest, cooridnateBounds.southeast],
    camera: initialCameraOptions,
    coordinatesPadding: coordinatesPadding,
    maxZoom: nil,
    offset: nil
)
amzada commented 2 months ago

@aleksproger I would still need to pass in my list of coordinates somehow. So coordinateBounds here is taking a screen rect however I would still like to pass in my array of coordinates (that draw up a route). So to summarise I need to pass in an array of coordinates + need them to be displayed in the top half of the screen so that they aren't being covered by my modal drawer view that's presented on top. Does that make sense?

amzada commented 2 months ago

hi @aleksproger was wondering if you had a chance to look at my comment above?

aleksproger commented 2 months ago

@amzada may you please provide a snippet of what methods you used before to achieve the desired result?

amzada commented 2 months ago

@aleksproger I was able to get an 'ok' solution in the past using the previous camera( for: coordinates, padding: edgeInsets, bearing: bearing, pitch: pitch ) Since padding was applied to both coordinates and screen. Now it becomes a bit tricky since we have coordinatesPadding and then somehow need to do some calculations to get the correct offset to apply. I just wonder if there's an easy way to say 'display these set of coordinates in this rectangle of the screen'

amzada commented 2 months ago

hi @aleksproger would just like to check on this one last time. Anything from your side please?

naftalibeder commented 1 month ago

@naftalibeder this should be fixed in 11.2 may you please verify and close the issue if it was resolved

@aleksproger My apologies for the delay on testing. I just tried with the SDK version 11.5.1, using the invocation

camera = try map.mapboxMap.camera(
  for: bounds,
  camera: .init(
    bearing: 0,
    pitch: 0
  ),
  coordinatesPadding: padding,
  maxZoom: nil,
  offset: nil
)

It has the exact same result as in my original description. Do you have any more information about this?

naftalibeder commented 2 weeks ago

I'm not sure what my previous test was doing, but the new method is actually working in v11! My mistake.

Note that the deprecated function is not working in v11, but that's not really a concern.