Skip to content

Commit 31bf0e0

Browse files
authored
Merge pull request #77 from TaskarCenterAtUW/feature-quest-contextual-info
Feature quest contextual info
2 parents e02d924 + 1d67024 commit 31bf0e0

File tree

2 files changed

+85
-2
lines changed

2 files changed

+85
-2
lines changed

GoInfoGame/GoInfoGame/UI/Map/CustomMap.swift

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import Foundation
99
import SwiftUI
1010
import MapKit
11+
import CoreLocation
1112

1213
// Custom Map for managing map interactions between SwiftUI and UIKit components
1314
struct CustomMap: UIViewRepresentable {
@@ -17,6 +18,9 @@ struct CustomMap: UIViewRepresentable {
1718
var items: [DisplayUnitWithCoordinate]
1819
@Binding var selectedQuest: DisplayUnit?
1920
@Binding var isPresented: Bool
21+
@StateObject var locationManagerDelegate = LocationManagerDelegate()
22+
23+
var contextualInfo: ((String) -> Void)?
2024

2125
// Creates and configures the UIView
2226
func makeUIView(context: Context) -> MKMapView {
@@ -47,8 +51,11 @@ struct CustomMap: UIViewRepresentable {
4751
class Coordinator: NSObject, MKMapViewDelegate {
4852
var parent: CustomMap
4953
var isRegionSet = false // boolean flag to track if region has been set
54+
var contextualInfo: ((String) -> Void)?
55+
5056
init(_ parent: CustomMap) {
5157
self.parent = parent
58+
self.contextualInfo = parent.contextualInfo
5259
}
5360

5461
// Customizes the view for each annotation
@@ -74,6 +81,17 @@ struct CustomMap: UIViewRepresentable {
7481
if let selectedQuest = annotation as? DisplayUnitAnnotation {
7582
parent.selectedQuest = selectedQuest.displayUnit
7683
parent.isPresented = true
84+
85+
let distance = parent.calculateDistance(selectedAnnotation: selectedQuest.coordinate)
86+
let direction = parent.inferDirection(selectedAnnotation: selectedQuest.coordinate)
87+
88+
let annotationLocation = CLLocation(latitude: selectedQuest.coordinate.latitude, longitude: selectedQuest.coordinate.longitude)
89+
parent.inferStreetName(location: annotationLocation) { streetName in
90+
if let streetName = streetName {
91+
let contextualString = "The \(selectedQuest.title!) is on \(streetName) at \(distance) meters \(direction) of you"
92+
self.contextualInfo?(contextualString)
93+
}
94+
}
7795
}
7896
// Deselect the annotation to prevent re-adding on selection
7997
mapView.deselectAnnotation(annotation, animated: false)
@@ -144,6 +162,70 @@ struct CustomMap: UIViewRepresentable {
144162
}
145163
}
146164

165+
// calculate distance between user current location and selected annotation
166+
func calculateDistance(selectedAnnotation: CLLocationCoordinate2D) -> CLLocationDistance {
167+
let userCurrentLocation = locationManagerDelegate.locationManager.location!.coordinate
168+
let fromLocation = CLLocation(latitude: userCurrentLocation.latitude, longitude: userCurrentLocation.longitude)
169+
let toLocation = CLLocation(latitude: selectedAnnotation.latitude, longitude: selectedAnnotation.longitude)
170+
return CLLocationDistance(Int(fromLocation.distance(from: toLocation)))
171+
}
172+
173+
// infer direction
174+
func inferDirection(selectedAnnotation: CLLocationCoordinate2D) -> String {
175+
let userCurrentLocation = locationManagerDelegate.locationManager.location!.coordinate
176+
let userLocationPoint = MKMapPoint(userCurrentLocation)
177+
let destinationPoint = MKMapPoint(selectedAnnotation)
178+
let angleRadians = atan2(destinationPoint.y - userLocationPoint.y, destinationPoint.x - userLocationPoint.x)
179+
var angleDegrees = angleRadians * 180 / .pi
180+
angleDegrees += 90
181+
182+
if angleDegrees < 0 {
183+
angleDegrees += 360
184+
} else if angleDegrees >= 360 {
185+
angleDegrees -= 360
186+
}
187+
188+
angleDegrees = (angleDegrees * 10).rounded() / 10
189+
190+
var direction = ""
191+
192+
// Convert angle into relative direction
193+
if angleDegrees >= 337.5 || angleDegrees < 22.5 {
194+
direction = "ahead"
195+
} else if angleDegrees >= 22.5 && angleDegrees < 112.5 {
196+
direction = "right"
197+
} else if angleDegrees >= 112.5 && angleDegrees < 202.5 {
198+
direction = "behind"
199+
} else if angleDegrees >= 202.5 && angleDegrees < 292.5 {
200+
direction = "left"
201+
} else {
202+
direction = "right"
203+
}
204+
205+
return direction
206+
}
207+
208+
// infer street name from user location coordinates
209+
func inferStreetName(location: CLLocation,completion: @escaping (String?) -> Void) {
210+
211+
let geocoder = CLGeocoder()
212+
geocoder.reverseGeocodeLocation(location) { placemarks, error in
213+
guard let placemark = placemarks?.first else {
214+
completion("")
215+
return
216+
}
217+
var addressComponents: [String] = []
218+
if let streetNumber = placemark.subThoroughfare {
219+
addressComponents.append(streetNumber)
220+
}
221+
if let streetName = placemark.thoroughfare {
222+
addressComponents.append(streetName)
223+
}
224+
225+
let address = addressComponents.joined(separator: ", ")
226+
completion(address)
227+
}
228+
}
147229
}
148230

149231
// Extension to convert MapUserTrackingMode to MKUserTrackingMode

GoInfoGame/GoInfoGame/UI/Map/MapView.swift

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import Combine
1212

1313
struct MapView: View {
1414
@State var trackingMode: MapUserTrackingMode = MapUserTrackingMode.none
15-
@StateObject var locationManagerDelegate = LocationManagerDelegate()
1615
@Environment(\.presentationMode) private var presentationMode
1716
@AppStorage("isMapFromOnboarding") var isMapFromOnboarding: Bool = false
1817
@StateObject private var viewModel = MapViewModel()
@@ -24,7 +23,9 @@ struct MapView: View {
2423
trackingMode: $trackingMode,
2524
items: viewModel.items,
2625
selectedQuest: $viewModel.selectedQuest,
27-
isPresented: $isPresented)
26+
isPresented: $isPresented, contextualInfo: { contextualInfo in
27+
print(contextualInfo)
28+
})
2829
.edgesIgnoringSafeArea(.all)
2930

3031
if viewModel.isLoading {

0 commit comments

Comments
 (0)