// // QSLRoadController.swift // QuickSearchLocation // // Created by Destiny on 2024/11/29. // import UIKit import MAMapKit import AMapFoundationKit import AMapLocationKit import BRPickerView enum QSLRoadJumpType: Int { case home case friend } class QSLRoadController: QSLBaseController { var type: QSLRoadJumpType? var userModel: QSLUserModel? // 高德地图 lazy var roadMapView = { let _mapView = MAMapView() _mapView.delegate = self _mapView.minZoomLevel = 7; _mapView.maxZoomLevel = 19; _mapView.zoomLevel = 17; _mapView.showsCompass = false _mapView.showsScale = false _mapView.isRotateCameraEnabled = false return _mapView }() /// 高德地图定位 lazy var roadMapLocationM = { let _homeMapLocationM = AMapLocationManager() _homeMapLocationM.delegate = self _homeMapLocationM.distanceFilter = 100 _homeMapLocationM.locatingWithReGeocode = true _homeMapLocationM.desiredAccuracy = kCLLocationAccuracyHundredMeters _homeMapLocationM.locationTimeout = 2 _homeMapLocationM.reGeocodeTimeout = 2 _homeMapLocationM.pausesLocationUpdatesAutomatically = false _homeMapLocationM.allowsBackgroundLocationUpdates = true return _homeMapLocationM }() lazy var roadManager: MATraceManager = { let manager = MATraceManager() return manager }() lazy var roadLine: MAPolyline = { let line = MAPolyline() return line }() var beginAnnotation: MAPointAnnotation? var endAnnotation: MAPointAnnotation? var roadList: [QSLMapTrackModel]? lazy var backButton: UIButton = { let btn = UIButton() btn.setBackgroundImage(UIImage(named: "route_back_btn"), for: .normal) btn.addTarget(self, action: #selector(backBtnAction), for: .touchUpInside) return btn }() lazy var foldBtn: UIButton = { let btn = UIButton() // btn.isSelected = false btn.touchExtendInset = UIEdgeInsets(top: -20, left: -20, bottom: -20, right: -20) btn.addRadius(radius: 2.rpx) btn.backgroundColor = .hexStringColor(hexString: "#000000", alpha: 0.7) btn.addTarget(self, action: #selector(foldBtnAction), for: .touchUpInside) return btn }() lazy var mainView: QSLRoadMainView = { let view = QSLRoadMainView() view.delegate = self return view }() lazy var startDatePicker: BRDatePickerView = { let datePicker = BRDatePickerView() datePicker.pickerMode = .YMDHM datePicker.maxDate = self.nowDate let style = BRPickerStyle() style.topCornerRadius = 10.rpx style.doneTextColor = QSLColor.themeMainColor style.selectRowColor = .hexStringColor(hexString: "#EDFFF9") style.selectRowTextColor = QSLColor.themeMainColor style.clearPickerNewStyle = false datePicker.pickerStyle = style return datePicker }() lazy var endDatePicker: BRDatePickerView = { let datePicker = BRDatePickerView() datePicker.pickerMode = .YMDHM datePicker.maxDate = self.nowDate let style = BRPickerStyle() style.topCornerRadius = 10.rpx style.doneTextColor = QSLColor.themeMainColor style.selectRowColor = .hexStringColor(hexString: "#EDFFF9") style.selectRowTextColor = QSLColor.themeMainColor style.clearPickerNewStyle = false datePicker.pickerStyle = style return datePicker }() // 开始日期 var startDate: Date = Date(timeIntervalSinceNow: -60 * 60 * 24) // 结束日期 var endDate: Date = Date() // 现在日期 var nowDate: Date = Date() init(userModel: QSLUserModel?) { super.init(nibName: nil, bundle: nil) self.userModel = userModel } @MainActor required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func viewDidLoad() { super.viewDidLoad() initView() if self.userModel?.remark.count != 0 { self.mainView.titleLabel.text = "\(self.userModel?.remark ?? "")的轨迹" } else { self.mainView.titleLabel.text = "\(self.userModel?.phone ?? "")的轨迹" } self.mainView.startTimeLabel.text = startDate.toformatterTimeString(formatter: "开始时间:yyyy-MM-dd HH:mm") self.mainView.endTimeLabel.text = endDate.toformatterTimeString(formatter: "结束时间:yyyy-MM-dd HH:mm") requestRoad() if let type = self.type { if type == .home { gravityInstance?.track(QSLGravityConst.road_show, properties: ["id": 1001]) } else { gravityInstance?.track(QSLGravityConst.road_show, properties: ["id": 1002]) } } } } extension QSLRoadController { @objc func foldBtnAction() { if !foldBtn.isSelected { UIView.animate(withDuration: 0.2) { self.mainView.qsl_y = QSLConst.qsl_kScreenH - 61.rpx self.foldBtn.qsl_y = QSLConst.qsl_kScreenH - 61.rpx - 6.rpx - 4.rpx } } else { UIView.animate(withDuration: 0.2) { self.mainView.qsl_y = QSLConst.qsl_kScreenH - 369.rpx self.foldBtn.qsl_y = QSLConst.qsl_kScreenH - 369.rpx - 6.rpx - 4.rpx } } foldBtn.isSelected = !foldBtn.isSelected } } extension QSLRoadController { // 查询轨迹 func requestRoad() { self.roadMapView.remove(self.roadLine) self.roadMapView.removeAnnotations(self.roadMapView.annotations) let start = self.startDate.timeIntervalSince1970 * 1000 let end = self.endDate.timeIntervalSince1970 * 1000 var dict: [String: Any] = [String: Any]() dict["userId"] = self.userModel?.friendId dict["start"] = start dict["end"] = end QSLLoading.show() QSLNetwork().request(.locationTrackQuery(dict:dict)) { resposne in QSLLoading.hide() let list = resposne.mapArray(QSLMapTrackModel.self, modelKey: "data>list") self.roadList = list if list.count == 0 { QSLAlertView.alert(view: self.view, title: "温馨提示", content: "该时段内未查询到历史轨迹记录:\n\n1.可能是该用户未登录本软件;\n2.可能是该用户未运行我们的软件;\n 3.可能是对方未开启定位和网络连接等原因。", isOneBtn: true, oneBtnText: "我知道了") } else { self.drawLine() } } fail: { code, error in QSLLoading.hide() self.view.toast(text: error) } } } // MARK: - 设置地图相关方法 extension QSLRoadController:MAMapViewDelegate, AMapLocationManagerDelegate { // 初始化高德地图设置 func setUpMap() { AMapServices.shared().enableHTTPS = true AMapLocationManager.updatePrivacyShow(.didShow, privacyInfo: .didContain) AMapLocationManager.updatePrivacyAgree(.didAgree) roadMapLocationM.startUpdatingLocation() } // 申请地图定位权限 func mapViewRequireLocationAuth(_ locationManager: CLLocationManager!) { locationManager.requestWhenInUseAuthorization() } func mapView(_ mapView: MAMapView!, viewFor annotation: (any MAAnnotation)!) -> MAAnnotationView! { if annotation is MAPointAnnotation { let reuseIdentifier = "pointReuseIdentifier" // 尝试从缓存池中重用AnnotationView var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: reuseIdentifier) as? QSLHomeAnnotatinView if annotationView == nil { annotationView = QSLHomeAnnotatinView(annotation: annotation, reuseIdentifier: reuseIdentifier) annotationView?.title = annotation.title } // 设置AnnotationView属性 annotationView?.isEnabled = false annotationView?.image = UIImage(named: annotation.subtitle ?? "") annotationView?.canShowCallout = false // 设置偏移量 annotationView?.centerOffset = CGPoint(x: 0, y: -18) annotationView?.calloutOffset = CGPoint(x: 0, y: -5) // 判断标题是否是“终点”,决定是否选中 annotationView?.isSelected = true return annotationView } return nil } func mapView(_ mapView: MAMapView!, didSingleTappedAt coordinate: CLLocationCoordinate2D) { /*// 直接获取被点击的轨迹线(支持多段线同时选中) let hittedPolylines = mapView.getHittedPolylines(with: coordinate, traverseAll: false) if let polyline = hittedPolylines?.first as? MAPolyline { print("点击了轨迹线,包含 \(polyline.pointCount) 个点") ///点击轨迹 if QSLBaseManager.shared.isVip() { gravityInstance?.track(QSLGravityConst.vip_location_click_Track, properties: ["id": 03006]) } else { gravityInstance?.track(QSLGravityConst.location_click_Track, properties: ["id": 03007]) } }*/ } // // 查询个人位置 // func searchMineLocation() { // // self.roadMapView.remove(self.roadLine) // self.roadMapView.removeAnnotations(self.roadMapView.annotations) // // self.roadMapLocationM.requestLocation(withReGeocode: true) { location, regeocode, error in // // if let error = error as? NSError { // // if error.code == AMapLocationErrorCode.locateFailed.rawValue { // return // } // } // // let beginPoint = MAPointAnnotation() // if let location = location { // let beginLocation = CLLocationCoordinate2D(latitude: location.coordinate.latitude, longitude: location.coordinate.latitude) // beginPoint.coordinate = beginLocation // beginPoint.title = self.userModel?.remark // beginPoint.subtitle = "" // self.beginAnnotation = beginPoint // // self.roadMapView.addAnnotation(self.beginAnnotation) // self.roadMapView.setCenter(location.coordinate, animated: true) // // print("当前位置:\(location)") // } // // if let regeocode = regeocode { // print("当前地址:\(regeocode)") // self.mainView.startAddressLabel.text = "起点:\(regeocode)" // } // } // } // // // 查询其他用户位置 // func searchOthersLocation() { // // self.roadMapView.remove(self.roadLine) // self.roadMapView.removeAnnotations(self.roadMapView.annotations) // // // if let model = self.userModel { // // QSLLoading.show() // QSLNetwork().request(.friendGet(dict: ["friendId": model.userId])) { response in // // QSLLoading.hide() // let user = response.mapObject(QSLUserModel.self, modelKey: "data") // var beginPoint = MAPointAnnotation() // var beginLocation = CLLocationCoordinate2D(latitude: model.location.lat, longitude: model.location.lng) // beginPoint.coordinate = beginLocation // beginPoint.title = self.userModel?.remark // beginPoint.subtitle = "" // self.beginAnnotation = beginPoint // // self.roadMapView.addAnnotation(self.beginAnnotation) // // self.roadMapView.setCenter(beginLocation, animated: true) // self.mainView.startAddressLabel.text = "起点:\(model.location.addr)" // } fail: { code, error in // // QSLLoading.hide() // self.view.toast(text: error) // } // } // } //绘制路线 func mapView(_ mapView: MAMapView!, rendererFor overlay: MAOverlay!) -> MAOverlayRenderer! { if overlay.isKind(of: MAPolyline.self) { let polylineRender = MAPolylineRenderer.init(overlay: overlay) polylineRender?.lineWidth = 4 polylineRender?.strokeColor = QSLColor.themeMainColor polylineRender?.fillColor = QSLColor.themeMainColor polylineRender?.lineJoinType = kMALineJoinRound polylineRender?.lineCapType = kMALineCapRound return polylineRender } return nil } // 绘制轨迹 func drawLine() { var MATraceList: [MATraceLocation] = [] guard let roadList = self.roadList else { return } for model in roadList { let location = MATraceLocation() location.speed = model.speed location.time = model.ts location.angle = model.bearing var loc = CLLocationCoordinate2D() loc.latitude = model.lat loc.longitude = model.lng location.loc = loc MATraceList.append(location) } self.roadManager.queryProcessedTrace(with: MATraceList, type: .aMap) { index, points in } finishCallback: { points, distance in print("轨迹纠偏success") // 纠偏成功添加纠偏后的折线 if let points = points { var commonPolylineCoords = [CLLocationCoordinate2D](repeating: CLLocationCoordinate2D(), count: points.count) for i in 0..