From 68b33e3564002995b1dcc6fde8ebefb7d93107a4 Mon Sep 17 00:00:00 2001 From: Naftali Beder Date: Wed, 31 Jan 2024 11:21:45 -0600 Subject: [PATCH 1/8] Zero padding when it's already baked into center/zoom from bounds --- ios/RNMBX/RNMBXCamera.swift | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/ios/RNMBX/RNMBXCamera.swift b/ios/RNMBX/RNMBXCamera.swift index 7c172fa7b..e621e3f4f 100644 --- a/ios/RNMBX/RNMBXCamera.swift +++ b/ios/RNMBX/RNMBXCamera.swift @@ -386,7 +386,7 @@ open class RNMBXCamera : RNMBXMapComponentBase { heading = CLLocationDirection(h) } - let padding = UIEdgeInsets( + var padding = UIEdgeInsets( top: stop["paddingTop"] as? Double ?? 0, left: stop["paddingLeft"] as? Double ?? 0, bottom: stop["paddingBottom"] as? Double ?? 0, @@ -394,8 +394,8 @@ open class RNMBXCamera : RNMBXMapComponentBase { ) var center: LocationCoordinate2D? + if let feature: String = stop["centerCoordinate"] as? String { - let centerFeature : Turf.Feature? = logged("RNMBXCamera.toUpdateItem.decode.cc") { try JSONDecoder().decode(Turf.Feature.self, from: feature.data(using: .utf8)!) } @@ -439,13 +439,14 @@ open class RNMBXCamera : RNMBXMapComponentBase { let camera = map.mapboxMap.camera( for: bounds, padding: padding, - bearing: heading ?? map.cameraState.bearing, - pitch: pitch ?? map.cameraState.pitch + bearing: heading ?? map.mapboxMap.cameraState.bearing, + pitch: pitch ?? map.mapboxMap.cameraState.pitch ) if let _center = camera.center, let _zoom = camera.zoom { center = _center zoom = _zoom + padding = .zero } } } From e9dbce247df441da6c5de7d94703763de687c4f1 Mon Sep 17 00:00:00 2001 From: Naftali Beder Date: Thu, 1 Feb 2024 12:14:36 -0600 Subject: [PATCH 2/8] Apply bounds camera directly to prevent padding being applied twice --- ios/RNMBX/RNMBXCamera.swift | 97 +++++++++++++++++++------------------ 1 file changed, 51 insertions(+), 46 deletions(-) diff --git a/ios/RNMBX/RNMBXCamera.swift b/ios/RNMBX/RNMBXCamera.swift index e621e3f4f..309ba4407 100644 --- a/ios/RNMBX/RNMBXCamera.swift +++ b/ios/RNMBX/RNMBXCamera.swift @@ -40,15 +40,21 @@ struct CameraUpdateItem { if let center = camera.center { try center.validate() } + + guard let duration = duration, duration > 0 else { + map.mapboxMap.setCamera(to: camera) + return + } + switch mode { - case .flight: + case .flight: map.mapView.camera.fly(to: camera, duration: duration) - case .ease: - map.mapView.camera.ease(to: camera, duration: duration ?? 0, curve: .easeInOut, completion: nil) - case .linear: - map.mapView.camera.ease(to: camera, duration: duration ?? 0, curve: .linear, completion: nil) - default: - map.mapboxMap.setCamera(to: camera) + case .ease: + map.mapView.camera.ease(to: camera, duration: duration, curve: .easeInOut, completion: nil) + case .linear: + map.mapView.camera.ease(to: camera, duration: duration, curve: .linear, completion: nil) + default: + map.mapboxMap.setCamera(to: camera) } } } @@ -371,10 +377,17 @@ open class RNMBXCamera : RNMBXMapComponentBase { if (stop.isEmpty) { return nil } + var zoom: CGFloat? if let z = stop["zoom"] as? Double { zoom = CGFloat(z) } + if let _minNS = minZoomLevel, let _min = CGFloat(exactly: _minNS), let _zoom = zoom, _zoom < _min { + zoom = _min + } + if let _maxNS = maxZoomLevel, let _max = CGFloat(exactly: _maxNS), let _zoom = zoom, _zoom < _max { + zoom = _max + } var pitch: CGFloat? if let p = stop["pitch"] as? Double { @@ -386,20 +399,22 @@ open class RNMBXCamera : RNMBXMapComponentBase { heading = CLLocationDirection(h) } - var padding = UIEdgeInsets( + var 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 center: LocationCoordinate2D? - if let feature: String = stop["centerCoordinate"] as? String { + 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 @@ -407,7 +422,16 @@ open class RNMBXCamera : RNMBXMapComponentBase { Logger.log(level: .error, message: "RNMBXCamera.toUpdateItem: Unexpected geometry: \(String(describing: centerFeature?.geometry))") return nil } - } else if let feature: String = stop["bounds"] as? String { + + 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 @@ -436,56 +460,37 @@ open class RNMBXCamera : RNMBXMapComponentBase { #else let bounds = CoordinateBounds(southwest: sw, northeast: ne) #endif - let camera = map.mapboxMap.camera( + + print(">>> \(padding?.top) + \(padding?.bottom) = \((padding?.top ?? 0) + (padding?.bottom ?? 0)) ... \(map.bounds.height)") + + camera = map.mapboxMap.camera( for: bounds, padding: padding, bearing: heading ?? map.mapboxMap.cameraState.bearing, pitch: pitch ?? map.mapboxMap.cameraState.pitch ) - - if let _center = camera.center, let _zoom = camera.zoom { - center = _center - zoom = _zoom - padding = .zero - } } } - let duration: TimeInterval? = { - if let d = stop["duration"] as? Double { - return toSeconds(d) - } + guard let camera = camera else { return nil - }() - - let mode: CameraMode = { - if let m = stop["mode"] as? String, let m = CameraMode(rawValue: m) { - return m - } - return .flight - }() + } - if let z1 = minZoomLevel, let z2 = CGFloat(exactly: z1), zoom ?? 100 < z2 { - zoom = z2 + var duration: TimeInterval? + if let d = stop["duration"] as? Double { + duration = toSeconds(d) } - - if let z1 = maxZoomLevel, let z2 = CGFloat(exactly: z1), zoom ?? 0 > z2 { - zoom = z2 + + var mode: CameraMode = .flight + if let m = stop["mode"] as? String, let m = CameraMode(rawValue: m) { + mode = m } - let result = CameraUpdateItem( - camera: CameraOptions( - center: center, - padding: padding, - anchor: nil, - zoom: zoom, - bearing: heading, - pitch: pitch - ), + return CameraUpdateItem( + camera: camera, mode: mode, duration: duration ) - return result } func _updateCamera() { From 7475156bc1860ef80b2f675373f8b20395a47722 Mon Sep 17 00:00:00 2001 From: Naftali Beder Date: Thu, 1 Feb 2024 12:15:58 -0600 Subject: [PATCH 3/8] Update example to allow granular control over padding --- example/src/examples/V10/CameraAnimation.tsx | 207 +++++++++---------- 1 file changed, 92 insertions(+), 115 deletions(-) diff --git a/example/src/examples/V10/CameraAnimation.tsx b/example/src/examples/V10/CameraAnimation.tsx index 1e1b6185f..f2eb51435 100644 --- a/example/src/examples/V10/CameraAnimation.tsx +++ b/example/src/examples/V10/CameraAnimation.tsx @@ -1,9 +1,8 @@ -import { Divider, Text } from '@rneui/base'; +import { CheckBox, Divider, Slider, Text } from '@rneui/base'; import { Camera, CameraAnimationMode, CameraBounds, - CameraPadding, CircleLayer, Logger, MapView, @@ -36,80 +35,47 @@ const initialCoordinate: Coordinate = { longitude: -73.984638, }; -const zeroPadding: CameraPadding = { - paddingTop: 0, - paddingBottom: 0, - paddingLeft: 0, - paddingRight: 0, -}; -const evenPadding: CameraPadding = { - paddingTop: 40, - paddingBottom: 40, - paddingLeft: 40, - paddingRight: 40, -}; const minZoomLevel = 8; const maxZoomLevel = 16; -const randPadding = (): CameraPadding => { - const randNum = () => { - const items = [0, 150, 300]; - return items[Math.floor(Math.random() * items.length)]; - }; - - return { - paddingTop: randNum(), - paddingBottom: randNum(), - paddingLeft: randNum(), - paddingRight: randNum(), - }; -}; - const toPosition = (coordinate: Coordinate): Position => { return [coordinate.longitude, coordinate.latitude]; }; const CameraAnimation = () => { - const [animationMode, setAnimationMode] = - useState('moveTo'); + const [easing, setEasing] = useState('easeTo'); const [coordinates, setCoordinates] = useState([ initialCoordinate, ]); - const [padding, setPadding] = useState(zeroPadding); - - const paddingDisplay = useMemo(() => { - return `L ${padding.paddingLeft} | R ${padding.paddingRight} | T ${padding.paddingTop} | B ${padding.paddingBottom}`; - }, [padding]); + const [paddingLeft, setPaddingLeft] = useState(0); + const [paddingRight, setPaddingRight] = useState(0); + const [paddingTop, setPaddingTop] = useState(0); + const [paddingBottom, setPaddingBottom] = useState(0); - const move = useCallback( - (_animationMode: CameraAnimationMode, shouldCreateMultiple: boolean) => { - setAnimationMode(_animationMode); - - if (shouldCreateMultiple) { - const _centerCoordinate = { + const move = useCallback((kind: 'center' | 'bounds') => { + if (kind === 'bounds') { + const _centerCoordinate = { + latitude: initialCoordinate.latitude + Math.random() * 0.2, + longitude: initialCoordinate.longitude + Math.random() * 0.2, + }; + const _coordinates = Array(10) + .fill(0) + .map((_) => { + return { + latitude: _centerCoordinate.latitude + Math.random() * 0.2, + longitude: _centerCoordinate.longitude + Math.random() * 0.2, + }; + }); + setCoordinates(_coordinates); + } else if (kind === 'center') { + setCoordinates([ + { latitude: initialCoordinate.latitude + Math.random() * 0.2, longitude: initialCoordinate.longitude + Math.random() * 0.2, - }; - const _coordinates = Array(10) - .fill(0) - .map((_) => { - return { - latitude: _centerCoordinate.latitude + Math.random() * 0.2, - longitude: _centerCoordinate.longitude + Math.random() * 0.2, - }; - }); - setCoordinates(_coordinates); - } else { - setCoordinates([ - { - latitude: initialCoordinate.latitude + Math.random() * 0.2, - longitude: initialCoordinate.longitude + Math.random() * 0.2, - }, - ]); - } - }, - [], - ); + }, + ]); + } + }, []); const features = useMemo((): Feature[] => { return coordinates.map((p) => { @@ -152,19 +118,47 @@ const CameraAnimation = () => { } }, [coordinates]); - const locationDisplay = useMemo(() => { - if (coordinates.length > 1) { - const ne = centerOrBounds.bounds?.ne.map((n) => n.toFixed(3)); - const sw = centerOrBounds.bounds?.sw.map((n) => n.toFixed(3)); - return `ne ${ne} | sw ${sw}`; - } else if (coordinates.length === 1) { - const lon = coordinates[0].longitude.toFixed(4); - const lat = coordinates[0].latitude.toFixed(4); - return `lon ${lon} | lat ${lat}`; - } else { - throw new Error('invalid location passed'); - } - }, [coordinates, centerOrBounds]); + const easingCheckBox = useCallback( + (value: CameraAnimationMode, label: string) => { + return ( + + {label} + setEasing(value)} + containerStyle={{ backgroundColor: 'transparent' }} + /> + + ); + }, + [easing], + ); + + const paddingCounter = useCallback( + (value: number, setValue: (value: number) => void, label: string) => { + return ( + + + {label} + {`${Math.round(value)}`} + + setValue(_value)} + /> + + ); + }, + [], + ); return ( <> @@ -174,9 +168,14 @@ const CameraAnimation = () => { zoomLevel={12} minZoomLevel={minZoomLevel} maxZoomLevel={maxZoomLevel} - padding={padding} + padding={{ + paddingTop, + paddingBottom, + paddingLeft, + paddingRight, + }} animationDuration={800} - animationMode={animationMode} + animationMode={easing} /> {features.map((feature) => { @@ -192,53 +191,31 @@ const CameraAnimation = () => { - centerCoordinate + Coordinate -