Skip to content

Commit 497ab5f

Browse files
authored
Merge pull request #23 from hactar/enhancement/camera-bounding-box
Add support for camera bounding box
2 parents 6b59504 + dfc7526 commit 497ab5f

File tree

6 files changed

+74
-7
lines changed

6 files changed

+74
-7
lines changed

Sources/MapLibreSwiftUI/Extensions/MapLibre/MLNMapViewCameraUpdating.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,12 @@ protocol MLNMapViewCameraUpdating: AnyObject {
1313
direction: CLLocationDirection,
1414
animated: Bool)
1515
func setZoomLevel(_ zoomLevel: Double, animated: Bool)
16+
func setVisibleCoordinateBounds(
17+
_ bounds: MLNCoordinateBounds,
18+
edgePadding: UIEdgeInsets,
19+
animated: Bool,
20+
completionHandler: (() -> Void)?
21+
)
1622
}
1723

1824
extension MLNMapView: MLNMapViewCameraUpdating {

Sources/MapLibreSwiftUI/MapViewCoordinator.swift

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,12 @@ public class MapViewCoordinator: NSObject {
6363
case .trackingUserLocationWithCourse:
6464
mapView.userTrackingMode = .followWithCourse
6565
mapView.setZoomLevel(camera.zoom, animated: false)
66-
case .rect, .showcase:
66+
case let .rect(boundingBox, padding):
67+
mapView.setVisibleCoordinateBounds(boundingBox,
68+
edgePadding: padding,
69+
animated: animated,
70+
completionHandler: nil)
71+
case .showcase:
6772
// TODO: Need a method these/or to finalize a goal here.
6873
break
6974
}

Sources/MapLibreSwiftUI/Models/MapCamera/CameraState.swift

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,10 @@ public enum CameraState: Hashable {
2525
case trackingUserLocationWithCourse
2626

2727
/// Centered on a bounding box/rectangle.
28-
case rect(northeast: CLLocationCoordinate2D, southwest: CLLocationCoordinate2D) // TODO: make a bounding box?
28+
case rect(
29+
boundingBox: MLNCoordinateBounds,
30+
edgePadding: UIEdgeInsets = .init(top: 20, left: 20, bottom: 20, right: 20)
31+
)
2932

3033
/// Showcasing GeoJSON, Polygons, etc.
3134
case showcase(shapeCollection: MLNShapeCollection)
@@ -42,10 +45,30 @@ extension CameraState: CustomDebugStringConvertible {
4245
"CameraState.trackingUserLocationWithHeading"
4346
case .trackingUserLocationWithCourse:
4447
"CameraState.trackingUserLocationWithCourse"
45-
case let .rect(northeast: northeast, southwest: southwest):
46-
"CameraState.rect(northeast: \(northeast), southwest: \(southwest))"
48+
case let .rect(boundingBox: boundingBox, _):
49+
"CameraState.rect(northeast: \(boundingBox.ne), southwest: \(boundingBox.sw))"
4750
case let .showcase(shapeCollection: shapeCollection):
4851
"CameraState.showcase(shapeCollection: \(shapeCollection))"
4952
}
5053
}
5154
}
55+
56+
extension MLNCoordinateBounds: Equatable, Hashable {
57+
public func hash(into hasher: inout Hasher) {
58+
hasher.combine(ne)
59+
hasher.combine(sw)
60+
}
61+
62+
public static func == (lhs: MLNCoordinateBounds, rhs: MLNCoordinateBounds) -> Bool {
63+
lhs.ne == rhs.ne && lhs.sw == rhs.sw
64+
}
65+
}
66+
67+
extension UIEdgeInsets: Hashable {
68+
public func hash(into hasher: inout Hasher) {
69+
hasher.combine(left)
70+
hasher.combine(right)
71+
hasher.combine(top)
72+
hasher.combine(bottom)
73+
}
74+
}

Sources/MapLibreSwiftUI/Models/MapCamera/MapViewCamera.swift

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,5 +118,21 @@ public struct MapViewCamera: Hashable {
118118
lastReasonForChange: .programmatic)
119119
}
120120

121-
// TODO: Create init methods for other camera states once supporting materials are understood (e.g. BoundingBox)
121+
/// Positions the camera to show a specific region in the MapView.
122+
///
123+
/// - Parameters:
124+
/// - box: Set the desired bounding box. This is a one time event and the user can manipulate by moving the map.
125+
/// - edgePadding: Set the edge insets that should be applied before positioning the map.
126+
/// - Returns: The MapViewCamera representing the scenario
127+
public static func boundingBox(
128+
_ box: MLNCoordinateBounds,
129+
edgePadding: UIEdgeInsets = .init(top: 20, left: 20, bottom: 20, right: 20)
130+
) -> MapViewCamera {
131+
// zoom, pitch & direction are ignored.
132+
MapViewCamera(state: .rect(boundingBox: box, edgePadding: edgePadding),
133+
zoom: 1,
134+
pitch: Defaults.pitch,
135+
direction: Defaults.direction,
136+
lastReasonForChange: .programmatic)
137+
}
122138
}

Tests/MapLibreSwiftUITests/Models/MapCamera/CameraStateTests.swift

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,9 @@ final class CameraStateTests: XCTestCase {
3535
let northeast = CLLocationCoordinate2D(latitude: 12.3, longitude: 23.4)
3636
let southwest = CLLocationCoordinate2D(latitude: 34.5, longitude: 45.6)
3737

38-
let state: CameraState = .rect(northeast: northeast, southwest: southwest)
39-
XCTAssertEqual(state, .rect(northeast: northeast, southwest: southwest))
38+
let state: CameraState = .rect(boundingBox: .init(sw: southwest, ne: northeast))
39+
XCTAssertEqual(state, .rect(boundingBox: .init(sw: southwest, ne: northeast)))
40+
4041
XCTAssertEqual(
4142
String(describing: state),
4243
"CameraState.rect(northeast: CLLocationCoordinate2D(latitude: 12.3, longitude: 23.4), southwest: CLLocationCoordinate2D(latitude: 34.5, longitude: 45.6))"

Tests/MapLibreSwiftUITests/Models/MapCamera/MapViewCameraTests.swift

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import CoreLocation
2+
import MapLibre
23
import XCTest
34
@testable import MapLibreSwiftUI
45

@@ -45,5 +46,20 @@ final class MapViewCameraTests: XCTestCase {
4546
XCTAssertEqual(camera.direction, 0)
4647
}
4748

49+
func testBoundingBox() {
50+
let southwest = CLLocationCoordinate2D(latitude: 24.6056011, longitude: 46.67369842529297)
51+
let northeast = CLLocationCoordinate2D(latitude: 24.6993808, longitude: 46.7709285)
52+
let bounds = MLNCoordinateBounds(sw: southwest, ne: northeast)
53+
let camera = MapViewCamera.boundingBox(bounds)
54+
55+
XCTAssertEqual(
56+
camera.state,
57+
.rect(boundingBox: bounds, edgePadding: .init(top: 20, left: 20, bottom: 20, right: 20))
58+
)
59+
XCTAssertEqual(camera.zoom, 1)
60+
XCTAssertEqual(camera.pitch, .free)
61+
XCTAssertEqual(camera.direction, 0)
62+
}
63+
4864
// TODO: Add additional camera tests once behaviors are added (e.g. rect)
4965
}

0 commit comments

Comments
 (0)