Send location uncertainty within a geo uri (#1309)

* Add uncertainty

* Add UTs

* Delete verticalAccuracy
This commit is contained in:
Alfonso Grillo
2023-07-12 11:02:05 +02:00
committed by GitHub
parent c573892954
commit adb253ff66
6 changed files with 57 additions and 11 deletions
@@ -57,6 +57,9 @@ struct MapLibreMapView: UIViewRepresentable {
@Binding var mapCenterCoordinate: CLLocationCoordinate2D? @Binding var mapCenterCoordinate: CLLocationCoordinate2D?
@Binding var isLocationAuthorized: Bool? @Binding var isLocationAuthorized: Bool?
// The radius of uncertainty for the location, measured in meters.
@Binding var geolocationUncertainty: CLLocationAccuracy?
/// Called when the user pan on the map /// Called when the user pan on the map
var userDidPan: (() -> Void)? var userDidPan: (() -> Void)?
@@ -155,6 +158,7 @@ extension MapLibreMapView {
} }
previousUserLocation = userLocation previousUserLocation = userLocation
updateGeolocationUncertainty(location: userLocation)
} }
func mapView(_ mapView: MGLMapView, didChangeLocationManagerAuthorization manager: MGLLocationManager) { func mapView(_ mapView: MGLMapView, didChangeLocationManagerAuthorization manager: MGLLocationManager) {
@@ -176,13 +180,7 @@ extension MapLibreMapView {
mapLibreView.mapCenterCoordinate = mapView.centerCoordinate mapLibreView.mapCenterCoordinate = mapView.centerCoordinate
} }
} }
// MARK: Callout
func mapView(_ mapView: MGLMapView, annotationCanShowCallout annotation: MGLAnnotation) -> Bool {
false
}
func mapView(_ mapView: MGLMapView, shouldChangeFrom oldCamera: MGLMapCamera, to newCamera: MGLMapCamera, reason: MGLCameraChangeReason) -> Bool { func mapView(_ mapView: MGLMapView, shouldChangeFrom oldCamera: MGLMapCamera, to newCamera: MGLMapCamera, reason: MGLCameraChangeReason) -> Bool {
// we send the userDidPan event only for the reasons that actually will change the map center, and not zoom only / rotations only events. // we send the userDidPan event only for the reasons that actually will change the map center, and not zoom only / rotations only events.
switch reason { switch reason {
@@ -203,6 +201,23 @@ extension MapLibreMapView {
} }
return true return true
} }
// MARK: Callout
func mapView(_ mapView: MGLMapView, annotationCanShowCallout annotation: MGLAnnotation) -> Bool {
false
}
// MARK: Private
private func updateGeolocationUncertainty(location: MGLUserLocation) {
guard let clLocation = location.location, clLocation.horizontalAccuracy >= 0 else {
mapLibreView.geolocationUncertainty = nil
return
}
mapLibreView.geolocationUncertainty = clLocation.horizontalAccuracy
}
} }
} }
@@ -109,6 +109,7 @@ struct StaticLocationScreenViewState: BindableState {
struct StaticLocationScreenBindings { struct StaticLocationScreenBindings {
var mapCenterLocation: CLLocationCoordinate2D? var mapCenterLocation: CLLocationCoordinate2D?
var geolocationUncertainty: CLLocationAccuracy?
var showsUserLocationMode: ShowUserLocationMode var showsUserLocationMode: ShowUserLocationMode
@@ -36,7 +36,8 @@ class StaticLocationScreenViewModel: StaticLocationScreenViewModelType, StaticLo
actionsSubject.send(.close) actionsSubject.send(.close)
case .selectLocation: case .selectLocation:
guard let coordinate = state.bindings.mapCenterLocation else { return } guard let coordinate = state.bindings.mapCenterLocation else { return }
actionsSubject.send(.sendLocation(.init(coordinate: coordinate), isUserLocation: state.isSharingUserLocation)) let uncertainty = state.isSharingUserLocation ? context.geolocationUncertainty : nil
actionsSubject.send(.sendLocation(.init(coordinate: coordinate, uncertainty: uncertainty), isUserLocation: state.isSharingUserLocation))
case .userDidPan: case .userDidPan:
state.bindings.showsUserLocationMode = .show state.bindings.showsUserLocationMode = .show
case .centerToUser: case .centerToUser:
@@ -47,10 +47,12 @@ struct StaticLocationScreen: View {
showsUserLocationMode: $context.showsUserLocationMode, showsUserLocationMode: $context.showsUserLocationMode,
error: $context.mapError, error: $context.mapError,
mapCenterCoordinate: $context.mapCenterLocation, mapCenterCoordinate: $context.mapCenterLocation,
isLocationAuthorized: $context.isLocationAuthorized) { isLocationAuthorized: $context.isLocationAuthorized,
geolocationUncertainty: $context.geolocationUncertainty) {
context.send(viewAction: .userDidPan) context.send(viewAction: .userDidPan)
} }
.ignoresSafeArea(.all, edges: mapSafeAreaEdges) .ignoresSafeArea(.all, edges: mapSafeAreaEdges)
if context.viewState.isLocationPickerMode { if context.viewState.isLocationPickerMode {
LocationMarkerView() LocationMarkerView()
} }
@@ -78,8 +78,8 @@ private extension RegexGeoURI {
} }
extension GeoURI { extension GeoURI {
init(coordinate: CLLocationCoordinate2D) { init(coordinate: CLLocationCoordinate2D, uncertainty: CLLocationAccuracy?) {
self.init(latitude: coordinate.latitude, longitude: coordinate.longitude) self.init(latitude: coordinate.latitude, longitude: coordinate.longitude, uncertainty: uncertainty)
} }
} }
@@ -76,4 +76,31 @@ class StaticLocationScreenViewModelTests: XCTestCase {
let authorizationError = AlertInfo(locationSharingViewError: .missingAuthorization) let authorizationError = AlertInfo(locationSharingViewError: .missingAuthorization)
XCTAssertEqual(authorizationError.message, L10n.errorMissingLocationAuth(InfoPlistReader.main.bundleDisplayName)) XCTAssertEqual(authorizationError.message, L10n.errorMissingLocationAuth(InfoPlistReader.main.bundleDisplayName))
} }
func testSendUserLocation() async throws {
context.mapCenterLocation = .init(latitude: 0, longitude: 0)
context.geolocationUncertainty = 10
let deferred = deferFulfillment(viewModel.actions.first())
context.send(viewAction: .selectLocation)
guard case .sendLocation(let geoUri, let isUserLocation) = try await deferred.fulfill() else {
XCTFail("Sent action should be 'sendLocation'")
return
}
XCTAssertEqual(geoUri.uncertainty, 10)
XCTAssertTrue(isUserLocation)
}
func testSendPickedLocation() async throws {
context.mapCenterLocation = .init(latitude: 0, longitude: 0)
context.isLocationAuthorized = nil
context.geolocationUncertainty = 10
let deferred = deferFulfillment(viewModel.actions.first())
context.send(viewAction: .selectLocation)
guard case .sendLocation(let geoUri, let isUserLocation) = try await deferred.fulfill() else {
XCTFail("Sent action should be 'sendLocation'")
return
}
XCTAssertEqual(geoUri.uncertainty, nil)
XCTAssertFalse(isUserLocation)
}
} }