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

Zoom level not applied correctly when switching from setting camera with centerCoordinate to bounds #3488

Open
RyszardRzepa opened this issue May 13, 2024 · 1 comment
Labels
bug 🪲 Something isn't working

Comments

@RyszardRzepa
Copy link

RyszardRzepa commented May 13, 2024

Mapbox Implementation

Mapbox

Mapbox Version

11.3.0

React Native Version

0.72.4

Platform

iOS, Android

@rnmapbox/maps version

10.1.19

Standalone component to reproduce

import React, { useRef } from "react";
import { Button, StyleSheet, TouchableOpacity, View, Text } from "react-native";
import Mapbox from "@rnmapbox/maps";
import { ShapeSource, CircleLayer, Camera } from "@rnmapbox/maps";


const App = () => {
const realMapRef = useRef<Mapbox.MapView>(null);
const realCameraRef = useRef<Mapbox.Camera>(null);

const currentZoom = 16;
const sheetHeight = 374;

const padding = {
      paddingTop: 60,
      paddingLeft: 24,
      paddingRight: 24,
      paddingBottom: sheetHeight ? sheetHeight - 30 : 30,
    };

// this function is called in the parent component. Left for the reference
const zoomToBounds = (bounds) => {
        realCameraRef.current.camera?.setCamera({
          bounds: {
            ne: [ 10.6701055, 59.9111846 ],
            sw:  [ 10.6270699, 59.9008117 ],
          },
          padding: padding,
          heading: 0.0, // reset to north
          animationMode: 'easeTo',
        });
      };
// this function is called in the parent component. Left for the reference
      const zoomToPosition = (pos) => {
        realCameraRef.current.setCamera({
          centerCoordinate: [ 10.6701055, 59.9008117 ],
          padding: padding,
          zoomLevel: currentZoom < 17 ? 17 : undefined,
          heading: 0.0,
          animationMode: 'easeTo',
        });
      };

  return (
  <Mapbox.MapView
  ref={realMapRef}
          styleURL="mapbox://styles/123"
          projection="globe"
          rotateEnabled={true}
          scaleBarEnabled={false}
          logoEnabled={false}
        >
           <Camera ref={realCameraRef} animationDuration={0} />
       {/*Rest of the code*/}
        </Mapbox.MapView>
  );
}

export default App;

Observed behavior and steps to reproduce

I see warning in Xcode when the bug happens: [Warning, maps-core]: {}[General]: Unable to calculate camera for given bounds/geometry, padding is greater than map's width or height.

We set the camera position and zoom level by using two methods, depends on what the user click.
If user click a marker in the map, we call zoomToPosition function where we set centerCoordinate with the marker position and the padding.

If user click on the fence we call zoomToBounds function where we use bounds param to set the camera in the center with the correct zoom.

The problem is that when we call zoomToPosition and the camera zoom in to the marker and later we call zoomToBounds the camera centers correctly but the zoom level is not applied to show all the markers within the bounds.

Simulator.Screen.Recording.-.iPhone.Xs.-.2024-05-13.at.11.27.06.mp4

const padding = {
  paddingTop: topSafeArea + 10,
  paddingLeft: 24,
  paddingRight: 24,
  paddingBottom: sheetHeight ? sheetHeight - 30 : 30,
};
      
 const zoomToPosition = (pos) => {
        realCameraRef.current.setCamera({
          centerCoordinate: [ 10.6701055, 59.9008117 ],
          padding: padding,
          zoomLevel: currentZoom < 17 ? 17 : undefined,
          heading: 0.0,
          animationMode: 'easeTo',
        });
      };
      
const zoomToBounds = (bounds) => {
        realCameraRef.current.camera?.setCamera({
          bounds: {
            ne: [ 10.6701055, 59.9111846 ],
            sw:  [ 10.6270699, 59.9008117 ],
          },
          padding: padding,
          heading: 0.0, // reset to north
          animationMode: 'easeTo',
        });
      };

Screen recording. Here we can see that clicking on the marker zoom in the camera, when we navigate back the zoomToBounds is called and it should zoom out the camera to show all the markers, but instead the zoom level don't change.
https://github.com/rnmapbox/maps/assets/13038459/bc2d5116-047d-49f7-b33b-d01ca54c2ae0

Expected behavior

Zoom level should be applied correctly to show all the markers within the bounds when calling first

  camera?.setCamera({
    centerCoordinate: pos,
    padding: padding,
    zoomLevel: currentZoom < 17 ? 17 : undefined,
    heading: 0.0,
    animationMode: 'easeTo',
  });

and calling after that setCamera with bounds

realCameraRef.current.camera?.setCamera({
          bounds: {
            ne: [ 10.6701055, 59.9111846 ],
            sw:  [ 10.6270699, 59.9008117 ],
          },
          padding: padding,
          heading: 0.0, // reset to north
          animationMode: 'easeTo',
        });
      };

Zoom level is not applied correctly when we use

Notes / preliminary analysis

We render the bottom sheet on the map. We use the bottom sheet height to calculate bottom padding, to make sure we center the camera in the visible part of the map.

It appears that this zoom issue is gone when we set the padding to the static value example: 30, but this will not center the camera in the visible part of the map.

Additional links and references

No response

Related issue #3354

@RyszardRzepa RyszardRzepa added the bug 🪲 Something isn't working label May 13, 2024
@RyszardRzepa RyszardRzepa changed the title Zoom level not updating when calling firstcamera.setCamera with centerCoordinate param and next time with bounds param. Zoom level not updating when calling first camera.setCamera with centerCoordinate param and next time with bounds param. May 13, 2024
@github-actions github-actions bot reopened this May 13, 2024
@RyszardRzepa RyszardRzepa changed the title Zoom level not updating when calling first camera.setCamera with centerCoordinate param and next time with bounds param. Zoom level not applied correctly when switching from setting camera with centerCoordinate to bounds May 13, 2024
@RyszardRzepa
Copy link
Author

RyszardRzepa commented May 14, 2024

In RNMBXCamera.siwft, If I reset the camera state before applying new bounds the padding error goes away and the zoom is applied as expected.

Screen recording showing expected behavior:
https://github.com/rnmapbox/maps/assets/13038459/47d7ce59-bc9c-430e-80d5-8e81f6e9ce22

Example code that fix the bug. I am not sure if this is the right way of doing it.

func resetCameraState(map: MapView) {
       // Reset camera options to default values
       let defaultCamera = CameraOptions(
           center: nil,
           padding: .zero,
           anchor: nil,
           zoom: nil,
           bearing: nil,
           pitch: nil
       )
       
       // Apply the default camera options to reset the camera state
       map.mapboxMap.setCamera(to: defaultCamera)
   }
   
   private func toUpdateItem(stop: [String: Any]) -> CameraUpdateItem? {
       if stop.isEmpty {
           return nil
       }

       var zoom: CGFloat?
       if let z = stop["zoom"] as? Double {
           zoom = CGFloat(z)
       }

       var pitch: CGFloat?
       if let p = stop["pitch"] as? Double {
           pitch = CGFloat(p)
       }

       var heading: CLLocationDirection?
       if let h = stop["heading"] as? Double {
           heading = CLLocationDirection(h)
       }

       let padding: UIEdgeInsets = UIEdgeInsets(
           top: stop["paddingTop"] as? Double ?? 0,
           left: stop["paddingLeft"] as? Double ?? 0,
           bottom: stop["paddingBottom"] as? Double ?? 0,
           right: stop["paddingRight"] as? Double ?? 0
       )

       var camera: CameraOptions?

       if let feature = stop["centerCoordinate"] as? String {
           let centerFeature: Turf.Feature? = logged("RNMBXCamera.toUpdateItem.decode.cc") {
               try JSONDecoder().decode(Turf.Feature.self, from: feature.data(using: .utf8)!)
           }

           var center: LocationCoordinate2D?

           switch centerFeature?.geometry {
           case .point(let centerPoint):
               center = centerPoint.coordinates
           default:
               Logger.log(level: .error, message: "RNMBXCamera.toUpdateItem: Unexpected geometry: \(String(describing: centerFeature?.geometry))")
               return nil
           }

           camera = CameraOptions(
               center: center,
               padding: padding,
               anchor: nil,
               zoom: zoom,
               bearing: heading,
               pitch: pitch
           )
       } else if let feature = stop["bounds"] as? String {
           let collection: Turf.FeatureCollection? = logged("RNMBXCamera.toUpdateItem.decode.bound") {
               try JSONDecoder().decode(Turf.FeatureCollection.self, from: feature.data(using: .utf8)!)
           }
           let features = collection?.features

           let ne: CLLocationCoordinate2D
           switch features?.first?.geometry {
           case .point(let point):
               ne = point.coordinates
           default:
               Logger.log(level: .error, message: "RNMBXCamera.toUpdateItem: Unexpected geometry: \(String(describing: features?.first?.geometry))")
               return nil
           }

           let sw: CLLocationCoordinate2D
           switch features?.last?.geometry {
           case .point(let point):
               sw = point.coordinates
           default:
               Logger.log(level: .error, message: "RNMBXCamera.toUpdateItem: Unexpected geometry: \(String(describing: features?.last?.geometry))")
               return nil
           }

           withMapView { map in
               // Reset the camera state before applying new bounds
               self.resetCameraState(map: map)

               #if RNMBX_11
               let bounds = [sw, ne]
               #else
               let bounds = CoordinateBounds(southwest: sw, northeast: ne)
               #endif

               camera = map.mapboxMap.camera(
                   for: bounds,
                   padding: padding,
                   bearing: heading ?? map.mapboxMap.cameraState.bearing,
                   pitch: pitch ?? map.mapboxMap.cameraState.pitch
               )
           }
       } else {
           withMapView { map in
               // Reset the camera state before applying new center coordinate
               self.resetCameraState(map: map)

               camera = CameraOptions(
                   center: nil,
                   padding: padding,
                   anchor: nil,
                   zoom: zoom,
                   bearing: heading,
                   pitch: pitch
               )
           }
       }

       guard let camera = camera else {
           return nil
       }

       var duration: TimeInterval?
       if let d = stop["duration"] as? Double {
           duration = toSeconds(d)
       }

       var mode: CameraMode = .flight
       if let m = stop["mode"] as? String, let m = CameraMode(rawValue: m) {
           mode = m
       }

       return CameraUpdateItem(
           camera: camera,
           mode: mode,
           duration: duration
       )
   }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug 🪲 Something isn't working
Projects
None yet
Development

No branches or pull requests

1 participant