-
Notifications
You must be signed in to change notification settings - Fork 4
/
MapViewController.swift
332 lines (272 loc) · 14.2 KB
/
MapViewController.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
//
// MapViewController.swift
// ARDemo
//
// Created by Ashis Laha on 13/07/17.
// Copyright © 2017 Ashis Laha. All rights reserved.
//
import UIKit
import GoogleMaps
import MapKit
enum Flow {
case createMarkerByLongPressAndShowDirection
case createMarkerByServerProvidedLocations
}
class MapViewController: UIViewController , UIGestureRecognizerDelegate {
@IBOutlet weak var appleMap: MKMapView!
@IBOutlet weak var googleMapCleanOutlet: UIBarButtonItem!
let defaultZoomLabel : Float = 19.0
let polylineStokeWidth : CGFloat = 5.0
private var mapView : GMSMapView!
private var userLocationMarker : GMSMarker!
private var polyline : GMSPolyline!
private var dropLocationMarker : GMSMarker!
private var flow : Flow = .createMarkerByLongPressAndShowDirection
private var paths : [[(Double,Double)]] = []
private var destination : (Double,Double) = (Double(),Double())
//MARK:- View Controller Life Cycle
override func viewDidLoad() {
super.viewDidLoad()
registerLocationManager()
//handleAppleMap()
handleGoogleMap()
registerNotification()
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return }
appDelegate.locationManager.stopUpdatingLocation()
NotificationCenter.default.removeObserver(self)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return }
appDelegate.locationManager.startUpdatingLocation()
reachabilityCheck()
}
private func reachabilityCheck() {
if !ReachabilityHelper.isInternetAvailable() {
let alert = UIAlertController(title: "No Internet", message: "Please connect to Internet", preferredStyle: .alert)
let okButton = UIAlertAction(title: "Ok", style: .default, handler: { (button) in
alert.dismiss(animated: true, completion: nil)
})
alert.addAction(okButton)
present(alert, animated: true, completion: nil)
}
}
private func registerLocationManager() {
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return }
appDelegate.locationManager.delegate = self
if CLLocationManager.authorizationStatus() == .authorizedWhenInUse {
appDelegate.locationManager.startUpdatingLocation()
appDelegate.locationManager.distanceFilter = kCLDistanceFilterNone
appDelegate.locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation
} else {
appDelegate.locationManager.requestWhenInUseAuthorization()
}
}
private func registerNotification() {
NotificationCenter.default.addObserver(forName: .UIApplicationWillEnterForeground, object: nil, queue: OperationQueue.main) { [weak self] (notification) in
self?.reachabilityCheck()
}
}
//MARK:- Apple Map Set up
private func handleAppleMap() {
self.appleMap.delegate = self
self.appleMap.showsUserLocation = true
let myLocation = CLLocationCoordinate2D(latitude: GPXFile.cherryHillPath.first?.0 ?? 0, longitude: GPXFile.cherryHillPath.first?.1 ?? 0)
let cabLocation = CLLocationCoordinate2D(latitude: GPXFile.cherryHillPath.last?.0 ?? 0, longitude: GPXFile.cherryHillPath.last?.1 ?? 0)
createRegion(coordinate: myLocation)
// add annotations
createAnnotation(location: cabLocation, title: "Cab location")
}
private func createAnnotation(location : CLLocationCoordinate2D, title : String ) {
let annotation = MKPointAnnotation()
annotation.coordinate = location
annotation.title = title
self.appleMap.addAnnotation(annotation)
}
private func createRegion(coordinate : CLLocationCoordinate2D) {
let span = MKCoordinateSpan(latitudeDelta: 0.001, longitudeDelta: 0.001)
let region = MKCoordinateRegion(center: coordinate, span: span)
self.appleMap.setRegion(region, animated: true)
}
// MARK:- Open AR View
@IBAction func openARView(_ sender: UIBarButtonItem) {
if let arVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "ARViewController") as? UINavigationController {
if let vc = arVC.visibleViewController as? ARViewController {
vc.sectionCoordinates = paths
vc.carLocation = destination
}
self.present(arVC, animated: true, completion: nil)
}
}
//MARK:- Google Map Set up
@IBAction func clearGoogleMap(_ sender: UIBarButtonItem) {
polyline?.map = nil
dropLocationMarker?.map = nil
paths = []
destination = (Double(),Double())
}
private func handleGoogleMap() {
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return }
if let userLocation = appDelegate.locationManager.location?.coordinate {
googleMapSetUp(location: userLocation)
}
if flow == .createMarkerByLongPressAndShowDirection {
mapView.delegate = self
title = "Tap On Map"
} else if flow == .createMarkerByServerProvidedLocations {
let fromLocation = CLLocationCoordinate2D(latitude: GPXFile.cherryHillPath.first?.0 ?? 0, longitude: GPXFile.cherryHillPath.first?.1 ?? 0)
let cabLocation = CLLocationCoordinate2D(latitude: GPXFile.cherryHillPath.last?.0 ?? 0, longitude: GPXFile.cherryHillPath.last?.1 ?? 0)
let _ = createMarker(location: fromLocation, mapView: mapView, markerTitle: "From Location", snippet: "")
let _ = createMarker(location: cabLocation, mapView: mapView, markerTitle: "Cab Location", snippet: "Waiting...")
drawPath(map: mapView, pathArray: GPXFile.cherryHillPath)
}
}
private func googleMapSetUp(location : CLLocationCoordinate2D) {
let camera = GMSCameraPosition.camera(withLatitude: location.latitude, longitude: location.longitude, zoom: defaultZoomLabel)
mapView = GMSMapView.map(withFrame: self.view.bounds, camera: camera)
mapView.isUserInteractionEnabled = true
mapView.autoresizingMask = [.flexibleHeight, .flexibleWidth]
view.addSubview(mapView)
}
private func createMarker(location : CLLocationCoordinate2D, mapView : GMSMapView, markerTitle : String, snippet : String, image : UIImage? = nil, markerName : String? = nil) -> GMSMarker {
let marker = GMSMarker(position: location)
marker.title = markerTitle
marker.snippet = snippet
if let image = image {
marker.icon = image
marker.groundAnchor = CGPoint(x: 0.5, y: 1.0)
}
if let markerName = markerName {
marker.userData = markerName
}
marker.map = mapView
return marker
}
private func removeMarker(marker : GMSMarker) {
marker.map = nil
}
private func drawPath(map : GMSMapView, pathArray : [(Double, Double)]) {
let path = GMSMutablePath()
for each in pathArray {
path.add(CLLocationCoordinate2D(latitude: each.0, longitude: each.1))
}
let polyline = GMSPolyline(path: path)
polyline.strokeColor = UIColor.blue
polyline.strokeWidth = polylineStokeWidth
polyline.geodesic = true
polyline.map = map
}
}
extension MapViewController : MKMapViewDelegate , CLLocationManagerDelegate {
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
var annotationView : MKAnnotationView?
annotationView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: "annotation")
annotationView?.canShowCallout = true
annotationView?.isEnabled = true
return annotationView
}
private func sameLocation(location1 : CLLocationCoordinate2D, location2 : CLLocationCoordinate2D) -> Bool {
return location1.latitude == location2.latitude && location1.longitude == location2.longitude
}
func mapView(_ mapView: MKMapView, didUpdate userLocation: MKUserLocation) {
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if let location = locations.last {
createRegion(coordinate: location.coordinate)
if let userLocationMarker = self.userLocationMarker {
removeMarker(marker: userLocationMarker)
}
userLocationMarker = GMSMarker(position: location.coordinate)
userLocationMarker.title = "User Location"
userLocationMarker.snippet = ""
if let image = UIImage(named: "blue-dot") {
userLocationMarker.icon = image
userLocationMarker.groundAnchor = CGPoint(x: 0.5, y: 1.0)
}
userLocationMarker.map = mapView
}
}
// Getting a new heading
func locationManager(_ manager: CLLocationManager, didUpdateHeading newHeading: CLHeading) {
let rotation = newHeading.magneticHeading * Double.pi / 180
print("rotation : \(rotation)")
}
}
extension MapViewController : GMSMapViewDelegate {
//MARK:- Create marker on long press
func mapView(_ mapView: GMSMapView, didLongPressAt coordinate: CLLocationCoordinate2D) {
self.dropLocationMarker?.map = nil
self.dropLocationMarker = createMarker(location: coordinate, mapView: self.mapView, markerTitle: "Destination", snippet: "", image: UIImage(named: "drop-pin"))
reachabilityCheck()
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return }
if let userLocation = appDelegate.locationManager.location?.coordinate {
if ReachabilityHelper.isInternetAvailable() {
fetchRoute(source: userLocation, destination: coordinate, completionHandler: { [weak self] (polyline) in
if let polyline = polyline as? GMSPolyline {
// Add user location
let path = GMSMutablePath()
if let userlocation = self?.userLocationMarker.position {
path.add(userlocation)
}
// add rest of the co-ordinates
if let polyLinePath = polyline.path, polyLinePath.count() > 0 {
for i in 0..<polyLinePath.count() {
path.add(polyLinePath.coordinate(at: i))
}
}
let updatedPolyline = GMSPolyline(path: path)
updatedPolyline.strokeColor = UIColor.blue
updatedPolyline.strokeWidth = self?.polylineStokeWidth ?? 5.0
self?.polyline?.map = nil
self?.polyline = updatedPolyline
self?.polyline?.map = self?.mapView
// update path and destination
self?.destination = (coordinate.latitude, coordinate.longitude)
if let path = updatedPolyline.path {
var polylinePath : [(Double,Double)] = []
for i in 0..<path.count() {
let point = path.coordinate(at: i)
polylinePath.append((point.latitude,point.longitude))
}
self?.paths = []
self?.paths.append(polylinePath)
}
}
})
}
}
}
private func fetchRoute(source : CLLocationCoordinate2D, destination : CLLocationCoordinate2D , completionHandler : ((Any) -> ())? ) {
let origin = String(format: "%f,%f", source.latitude,source.longitude)
let destination = String(format: "%f,%f", destination.latitude,destination.longitude)
let directionsAPI = "https://maps.googleapis.com/maps/api/directions/json?"
let directionsUrlString = String(format: "%@&origin=%@&destination=%@&mode=driving",directionsAPI,origin,destination ) // walking , driving
if let url = URL(string: directionsUrlString) {
let fetchDirection = URLSession.shared.dataTask(with: url, completionHandler: { (data, response, error) in
DispatchQueue.main.async {
if error == nil && data != nil {
var polyline : GMSPolyline?
if let dictionary = try? JSONSerialization.jsonObject(with: data!, options: .allowFragments ) as? [String : Any] {
if let routesArray = dictionary?["routes"] as? [Any], !routesArray.isEmpty {
if let routeDict = routesArray.first as? [String : Any] , !routeDict.isEmpty {
if let routeOverviewPolyline = routeDict["overview_polyline"] as? [String : Any] , !routeOverviewPolyline.isEmpty {
if let points = routeOverviewPolyline["points"] as? String {
if let path = GMSPath(fromEncodedPath: points) {
polyline = GMSPolyline(path: path)
}
}
}
}
}
}
if let polyline = polyline { completionHandler?(polyline) }
}
}
})
fetchDirection.resume()
}
}
}