Skip to content

Commit

Permalink
Migrate mapbox v11 (#16)
Browse files Browse the repository at this point in the history
* Imported v11 (WIP)

* Fixed heatmap layer

* Improved snap functionality

* Cleanup

* Minor cleanup

---------

Co-authored-by: Pantelis Giazitsis <[email protected]>
  • Loading branch information
pantelisss and Pantelis Giazitsis authored Feb 8, 2024
1 parent 81da50f commit c0eedfb
Show file tree
Hide file tree
Showing 12 changed files with 83 additions and 140 deletions.
78 changes: 22 additions & 56 deletions PresentationLayer/UI Components/MapBox/MapBoxClaimDevice.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import UIKit
struct MapBoxClaimDeviceView: View {
@Binding var location: CLLocationCoordinate2D
@Binding var annotationTitle: String?
let areLocationServicesAvailable: Bool
let geometryProxyForFrameOfMapView: CGRect

private let markerSize: CGSize = CGSize(width: 44.0, height: 44.0)
Expand All @@ -24,18 +23,16 @@ struct MapBoxClaimDeviceView: View {

init(location: Binding<CLLocationCoordinate2D>,
annotationTitle: Binding<String?>,
areLocationServicesAvailable: Bool, geometryProxyForFrameOfMapView: CGRect) {
geometryProxyForFrameOfMapView: CGRect) {
_location = location
_annotationTitle = annotationTitle
self.areLocationServicesAvailable = areLocationServicesAvailable
self.geometryProxyForFrameOfMapView = geometryProxyForFrameOfMapView
}

var body: some View {
ZStack {
MapBoxClaimDevice(location: $location,
locationPoint: $locationPoint,
areLocationServicesAvailable: areLocationServicesAvailable,
geometryProxyForFrameOfMapView: geometryProxyForFrameOfMapView)

markerAnnotation
Expand Down Expand Up @@ -84,23 +81,13 @@ struct MapBoxClaimDevice: UIViewControllerRepresentable {
@Binding var location: CLLocationCoordinate2D
@Binding var locationPoint: CGPoint

let areLocationServicesAvailable: Bool
let geometryProxyForFrameOfMapView: CGRect

init(location: Binding<CLLocationCoordinate2D>, locationPoint: Binding<CGPoint> = .constant(.zero), areLocationServicesAvailable: Bool, geometryProxyForFrameOfMapView: CGRect) {
_location = location
_locationPoint = locationPoint
self.areLocationServicesAvailable = areLocationServicesAvailable
self.geometryProxyForFrameOfMapView = geometryProxyForFrameOfMapView
}

func makeUIViewController(context _: Context) -> MapViewLocationController {
return MapViewLocationController(
frame: geometryProxyForFrameOfMapView,
location: $location,
locationPoint: $locationPoint,
locationServicesAvailable: areLocationServicesAvailable
)
locationPoint: $locationPoint)
}

func updateUIViewController(_ controller: MapViewLocationController, context _: Context) {
Expand All @@ -114,25 +101,16 @@ class MapViewLocationController: UIViewController {
let frame: CGRect
@Binding var location: CLLocationCoordinate2D
@Binding var locationPoint: CGPoint
let locationServicesAvailable: Bool
internal var mapView: MapView!
private var cancelablesSet = Set<AnyCancelable>()

init(frame: CGRect, location: Binding<CLLocationCoordinate2D>, locationPoint: Binding<CGPoint>, locationServicesAvailable: Bool) {
init(frame: CGRect, location: Binding<CLLocationCoordinate2D>, locationPoint: Binding<CGPoint>) {
self.frame = frame
_location = location
_locationPoint = locationPoint
self.locationServicesAvailable = locationServicesAvailable
super.init(nibName: nil, bundle: nil)
}

init?(frame: CGRect, location: Binding<CLLocationCoordinate2D>, locationPoint: Binding<CGPoint>, locationServicesAvailable: Bool, coder aDecoder: NSCoder) {
self.frame = frame
_location = location
_locationPoint = locationPoint
self.locationServicesAvailable = locationServicesAvailable
super.init(coder: aDecoder)
}

@available(*, unavailable)
required init?(coder _: NSCoder) {
fatalError("fatal Error")
Expand All @@ -145,10 +123,9 @@ class MapViewLocationController: UIViewController {
return
}

let myResourceOptions = ResourceOptions(accessToken: accessToken)

let cameraOptions = cameraSetup()
let myMapInitOptions = MapInitOptions(
resourceOptions: myResourceOptions,
cameraOptions: cameraOptions
)

Expand All @@ -158,40 +135,29 @@ class MapViewLocationController: UIViewController {
mapView.ornaments.options.scaleBar.visibility = .hidden
mapView.gestures.options.rotateEnabled = false
mapView.gestures.options.pitchEnabled = false
mapView.mapboxMap.setCamera(to: CameraOptions(zoom: 2))
view.addSubview(mapView)

switch locationServicesAvailable {
case true:
mapView.location.delegate = self
mapView.location.locationProvider.startUpdatingLocation()
case false: break
}

mapView.mapboxMap.onNext(event: .mapLoaded) { [weak self] _ in
mapView.mapboxMap.onMapLoaded.observeNext { [weak self] _ in
guard let self else {
return
}

var pointAnnotation = PointAnnotation(coordinate: self.mapView.cameraState.center)
switch self.locationServicesAvailable {
case true:
self.locationUpdate(newLocation: self.mapView.location.latestLocation!)
pointAnnotation.point.coordinates = self.mapView.cameraState.center
case false:
break
}
let pointAnnotation = PointAnnotation(coordinate: self.mapView.mapboxMap.cameraState.center)
self.locationPoint = self.mapView.mapboxMap.point(for: self.location)
self.mapView.mapboxMap.onEvery(event: .cameraChanged, handler: { [weak self] _ in
guard let self = self else { return }
pointAnnotation.point.coordinates = self.mapView.cameraState.center
DispatchQueue.main.async { [weak self] in
if let self = self {
self.location = pointAnnotation.point.coordinates
self.locationPoint = mapView.mapboxMap.point(for: self.location)
}
}
})
}
}.store(in: &cancelablesSet)

self.mapView.mapboxMap.onMapIdle.observe { [weak self] _ in
guard let self = self else { return }
let pointAnnotation = PointAnnotation(coordinate: self.mapView.mapboxMap.cameraState.center)
DispatchQueue.main.async { [weak self] in
if let self = self {
self.location = pointAnnotation.point.coordinates
self.locationPoint = mapView.mapboxMap.point(for: self.location)
}
}
}.store(in: &cancelablesSet)

}

func setCenter(_ center: CLLocationCoordinate2D) {
Expand All @@ -207,7 +173,7 @@ class MapViewLocationController: UIViewController {
}
}

extension MapViewLocationController: LocationPermissionsDelegate, LocationConsumer {
extension MapViewLocationController {
func locationUpdate(newLocation: Location) {
mapView.mapboxMap.setCamera(to: CameraOptions(center: newLocation.coordinate, zoom: 13))
location = newLocation.coordinate
Expand Down
59 changes: 32 additions & 27 deletions PresentationLayer/UI Components/MapBox/MapBoxMapView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,27 @@ import Toolkit

struct MapBoxMapView: UIViewControllerRepresentable {
@EnvironmentObject var explorerViewModel: ExplorerViewModel
private var canellables: CancellableWrapper = .init()

func makeUIViewController(context: Context) -> MapViewController {
let mapViewController = explorerViewModel.mapController ?? MapViewController()
mapViewController.delegate = context.coordinator
explorerViewModel.mapController = mapViewController
observeSnapLocation()

return mapViewController
}

func updateUIViewController(_ mapViewController: MapViewController, context _: Context) {
if let location = explorerViewModel.locationToSnap {
mapViewController.snapToLocationCoordinates(location) {
// Reset `locationToSnap` to avoid snaps on every re-render
explorerViewModel.locationToSnap = nil
}
}
func observeSnapLocation() {
explorerViewModel.snapLocationPublisher.receive(on: DispatchQueue.main).sink { location in
guard let location else {
return
}
explorerViewModel.mapController?.snapToLocationCoordinates(location) {}
}.store(in: &canellables.cancellableSet)
}

func updateUIViewController(_ mapViewController: MapViewController, context _: Context) {
if explorerViewModel.showUserLocation {
mapViewController.showUserLocation()
}
Expand All @@ -54,9 +59,12 @@ extension MapBoxMapView {

class Coordinator: NSObject, MapViewControllerDelegate {
func didTapAnnotation(_: MapViewController, _ annotations: [PolygonAnnotation]) {
guard let firstValidAnnotation = annotations.first else { return }
guard let hexIndex = firstValidAnnotation.userInfo?.keys.first else { return }
viewModel.routeToDeviceListFor(hexIndex, firstValidAnnotation.polygon.center)
guard let firstValidAnnotation = annotations.first,
let hexIndex = firstValidAnnotation.userInfo?.keys.first else {
return
}

viewModel.routeToDeviceListFor(hexIndex, firstValidAnnotation.polygon.center)
}

func didTapMapArea(_: MapViewController) {
Expand All @@ -77,23 +85,21 @@ extension MapBoxMapView {
}
}

let parent: MapBoxMapView
let viewModel: ExplorerViewModel

init(_ mapBoxMapView: MapBoxMapView, viewModel: ExplorerViewModel) {
parent = mapBoxMapView
self.viewModel = viewModel
}
}
}

class MapViewController: UIViewController {
private static let SNAP_ANIMATION_DURATION: CGFloat = 1.4
private static let wxm_lat = 37.98075475244475
private static let wxm_lon = 23.710478235562956
private var cancelablesSet = Set<AnyCancelable>()

internal var mapView: MapView!
internal var layer = HeatmapLayer(id: "wtxm-heatmap-layer")
internal var mapView: MapView!
internal var layer = HeatmapLayer(id: MapBoxConstants.heatmapLayerId,
source: MapBoxConstants.heatmapSource)
internal weak var polygonManager: PolygonAnnotationManager?

weak var delegate: MapViewControllerDelegate?
Expand All @@ -107,8 +113,7 @@ class MapViewController: UIViewController {
return
}

let myResourceOptions = ResourceOptions(accessToken: accessToken)
let myMapInitOptions = MapInitOptions(resourceOptions: myResourceOptions, styleURI: StyleURI(rawValue: MapBoxConstants.mapBoxStyle))
let myMapInitOptions = MapInitOptions(styleURI: StyleURI(rawValue: MapBoxConstants.mapBoxStyle))

mapView = MapView(frame: view.bounds, mapInitOptions: myMapInitOptions)
mapView.ornaments.options.scaleBar.visibility = .hidden
Expand All @@ -117,10 +122,10 @@ class MapViewController: UIViewController {
mapView.gestures.singleTapGestureRecognizer.addTarget(self, action: #selector(didTapMap(_:)))

view.addSubview(mapView)
mapView.mapboxMap.onNext(.mapLoaded) { [weak self] _ in
mapView.mapboxMap.onMapLoaded.observeNext { [weak self] _ in
guard let self = self else { return }
self.cameraSetup()
}
}.store(in: &cancelablesSet)
}

override func viewWillAppear(_ animated: Bool) {
Expand All @@ -129,7 +134,6 @@ class MapViewController: UIViewController {
}

internal func configureHeatMapLayer(source: GeoJSONSource) {
layer.source = "wtxm-source"
layer.maxZoom = 10
layer.heatmapColor = .expression(
Exp(.interpolate) {
Expand Down Expand Up @@ -190,21 +194,22 @@ class MapViewController: UIViewController {
}
)
do {
try mapView.mapboxMap.style.addSource(source, id: "wtxm-source")
try mapView.mapboxMap.style.addLayer(layer)
try mapView.mapboxMap.addSource(source)
try mapView.mapboxMap.addLayer(layer)
} catch {
print(error)
}
}

internal func configurePolygonLayer(polygonAnnotations: [PolygonAnnotation]) {
let polygonAnnotationManager = self.polygonManager ?? mapView.annotations.makePolygonAnnotationManager(id: "wtxm-polygon-annotation-manager")
let polygonAnnotationManager = self.polygonManager ?? mapView.annotations.makePolygonAnnotationManager(id: MapBoxConstants.polygonManagerId)
polygonAnnotationManager.annotations = polygonAnnotations
polygonManager = polygonAnnotationManager
}

internal func cameraSetup() {
let centerCoordinate = CLLocationCoordinate2D(latitude: Self.wxm_lat, longitude: Self.wxm_lon)
let centerCoordinate = CLLocationCoordinate2D(latitude: MapBoxConstants.initialLat,
longitude: MapBoxConstants.initialLon)
let camera = CameraOptions(center: centerCoordinate, zoom: 1)
mapView.mapboxMap.setCamera(to: camera)
}
Expand All @@ -225,13 +230,13 @@ class MapViewController: UIViewController {
let annotations = polygonManager.annotations
let options = RenderedQueryOptions(layerIds: layerIds, filter: nil)
let point = tap.location(in: tap.view)
mapView.mapboxMap.queryRenderedFeatures(in: CGRect(origin: point, size: CGSize.zero).insetBy(dx: -20.0, dy: -20.0), options: options) { result in
mapView.mapboxMap.queryRenderedFeatures(with: CGRect(origin: point, size: CGSize.zero).insetBy(dx: -20.0, dy: -20.0), options: options) { result in
switch result {
case let .success(queriedFeatures):

// Get the identifiers of all the queried features
let queriedFeatureIds: [String] = queriedFeatures.compactMap {
guard case let .string(featureId) = $0.feature.identifier else {
guard case let .string(featureId) = $0.queriedFeature.feature.identifier else {
return nil
}
return featureId
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ struct ClaimDeviceLocationMapView: View {
MapBoxClaimDeviceView(
location: $viewModel.selectedCoordinates,
annotationTitle: Binding(get: { viewModel.selectedLocation?.name }, set: { _ in }),
areLocationServicesAvailable: false,
geometryProxyForFrameOfMapView: geometry.frame(in: .global)
)
.onTapGesture {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ public final class ExplorerViewModel: ObservableObject {
@Published var isLoading: Bool = false
@Published var isSearchActive: Bool = false

@Published var locationToSnap: MapBoxMapView.SnapLocation?
lazy var snapLocationPublisher: AnyPublisher<MapBoxMapView.SnapLocation?, Never> = snapLocationSubject.eraseToAnyPublisher()
private let snapLocationSubject = PassthroughSubject<MapBoxMapView.SnapLocation?, Never>()
@Published var showUserLocation: Bool = false
private var isInitialSnapped: Bool = false

Expand Down Expand Up @@ -85,7 +86,8 @@ public final class ExplorerViewModel: ObservableObject {
}
default:
if let suggestedLocation = explorerUseCase.getSuggestedDeviceLocation() {
locationToSnap = MapBoxMapView.SnapLocation(coordinates: suggestedLocation, zoomLevel: nil)
let locationToSnap = MapBoxMapView.SnapLocation(coordinates: suggestedLocation, zoomLevel: nil)
snapLocationSubject.send(locationToSnap)
}
}
}
Expand All @@ -111,7 +113,8 @@ private extension ExplorerViewModel {
switch result {
case .success(let coordinates):
let zoomLevel: CGFloat? = zoomEnabled ? MapBoxMapView.SnapLocation.DEFAULT_SNAP_ZOOM_LEVEL : nil
self.locationToSnap = MapBoxMapView.SnapLocation(coordinates: coordinates, zoomLevel: zoomLevel)
let locationToSnap = MapBoxMapView.SnapLocation(coordinates: coordinates, zoomLevel: zoomLevel)
self.snapLocationSubject.send(locationToSnap)
self.showUserLocation = true
case .failure(let error):
print(error)
Expand All @@ -136,7 +139,8 @@ extension ExplorerViewModel: ExplorerSearchViewModelDelegate {
}

func rowTapped(coordinates: CLLocationCoordinate2D, deviceId: String?, cellIndex: String?) {
locationToSnap = MapBoxMapView.SnapLocation(coordinates: coordinates)
let locationToSnap = MapBoxMapView.SnapLocation(coordinates: coordinates)
snapLocationSubject.send(locationToSnap)

if let deviceId, let cellIndex {
navigateToDeviceDetails(deviceId: deviceId, cellIndex: cellIndex, cellCenter: coordinates)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ struct SelectStationLocationView: View {
ZStack {
MapBoxClaimDeviceView(location: $viewModel.selectedCoordinate,
annotationTitle: Binding(get: { viewModel.selectedDeviceLocation?.name }, set: { _ in }),
areLocationServicesAvailable: false,
geometryProxyForFrameOfMapView: proxy.frame(in: .local))
.cornerRadius(CGFloat(.cardCornerRadius), corners: [.topLeft, .topRight])

Expand Down
5 changes: 5 additions & 0 deletions PresentationLayer/Utils/MapBox/MapBoxConstants.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,9 @@ enum MapBoxConstants {
static let snapshotSize: CGSize = CGSize(width: 340.0, height: 200.0)
static let snapshotZoom: CGFloat = 11.0
static let snapshotMarkerName = "marker"
static let polygonManagerId = "wtxm-polygon-annotation-manager"
static let initialLat = 37.98075475244475
static let initialLon = 23.710478235562956
static let heatmapLayerId = "wtxm-heatmap-layer"
static let heatmapSource = "heatmap"
}
2 changes: 1 addition & 1 deletion wxm-ios.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -3497,7 +3497,7 @@
repositoryURL = "https://github.com/mapbox/mapbox-maps-ios.git";
requirement = {
kind = upToNextMajorVersion;
minimumVersion = 10.0.0;
minimumVersion = 11.0.0;
};
};
261E9EB629A92BE000E8EBA3 /* XCRemoteSwiftPackageReference "Alamofire" */ = {
Expand Down
Loading

0 comments on commit c0eedfb

Please sign in to comment.