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

Setting camera bounds with padding fails [v11] #2134

Closed
naftalibeder opened this issue Feb 27, 2024 · 11 comments
Closed

Setting camera bounds with padding fails [v11] #2134

naftalibeder opened this issue Feb 27, 2024 · 11 comments
Labels
bug 🪲 Something is broken!

Comments

@naftalibeder
Copy link

naftalibeder commented Feb 27, 2024

Environment

  • Xcode version: 15.2
  • iOS version: 17.0
  • Devices affected: iOS Simulator
  • Maps SDK Version: Reproduced on 11.2.0-rc1 and 11.1.0

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

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

  • This bug appears to happen on all v11. On v10, it works correctly.
  • Using try camera(for:camera:coordinatesPadding:maxZoom:offset:) has the same issue, but it throws instead of quietly logging to the console.
  • Lowering the padding to total a smaller amount produces a strange set of bounds:
    • >=353: Zoom does not change, as shown in the v11 video.
    • =352: Suddenly zooms way out to bound around a very large area.
    • <=351: As padding gets closer to zero, zoom increases until eventually settles at the correct value when padding is 0.

Additional links and references

The above example behaves correctly on v10:

v10.mov

but fails on v11:

v11.mov
@aleksproger
Copy link
Contributor

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

@aleksproger
Copy link
Contributor

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
Copy link

amzada commented Jun 17, 2024

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
Copy link
Contributor

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
Copy link

amzada commented Jun 18, 2024

@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
Copy link

amzada commented Jun 20, 2024

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

@aleksproger
Copy link
Contributor

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

@amzada
Copy link

amzada commented Jun 20, 2024

@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
Copy link

amzada commented Jun 25, 2024

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

@naftalibeder
Copy link
Author

@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
Copy link
Author

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug 🪲 Something is broken!
Projects
None yet
Development

No branches or pull requests

3 participants