| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642 |
- //
- // 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) {
-
- ///点击轨迹
- if QSLBaseManager.shared.isVip() {
- gravityInstance?.track(QSLGravityConst.vip_location_click_Track, properties: ["id": 03006])
- } else {
- gravityInstance?.track(QSLGravityConst.location_click_Track, properties: ["id": 03007])
- }
- /*// 直接获取被点击的轨迹线(支持多段线同时选中)
- 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..<points.count {
- commonPolylineCoords[i].latitude = points[i].latitude
- commonPolylineCoords[i].longitude = points[i].longitude
- }
-
- // 构造折线对象
- if let commonPolyline = MAPolyline(coordinates: &commonPolylineCoords, count: UInt(points.count)) {
- self.roadLine = commonPolyline
-
- // 在地图上添加折线对象
- self.roadMapView.add(self.roadLine)
-
- //调用划线后,调用下面方法,设置地图可视矩形的范围
- self.roadMapView.setVisibleMapRect(self.roadLine.boundingMapRect, edgePadding: UIEdgeInsets(top: 40, left: 40, bottom: 40, right: 40), animated: true)
- }
-
- // 添加起点图标
- if let startTrack = points.first {
- let startPoint = MAPointAnnotation()
- startPoint.coordinate = CLLocationCoordinate2D(latitude: startTrack.latitude, longitude: startTrack.longitude)
- startPoint.title = "起点"
- startPoint.subtitle = "road_begin_icon"
- self.beginAnnotation = startPoint
- }
-
- // 添加终点图标
- if let endTrack = points.last {
- let endPoint = MAPointAnnotation()
- endPoint.coordinate = CLLocationCoordinate2D(latitude: endTrack.latitude, longitude: endTrack.longitude)
- endPoint.title = "终点"
- endPoint.subtitle = "road_end_icon"
- self.endAnnotation = endPoint
- }
-
- // 设置地址
- if let startModel = self.roadList?.first,
- let endModel = self.roadList?.last {
-
- self.mainView.startAddressLabel.text = "起点:\(startModel.addr)"
- self.mainView.endAddressLabel.text = "终点:\(endModel.addr)"
- }
-
- // 添加起点和终点图标
- if let startPoint = self.beginAnnotation,
- let endPoint = self.endAnnotation {
- self.roadMapView.addAnnotation(startPoint)
- self.roadMapView.addAnnotation(endPoint)
- }
- }
- } failedCallback: { code, error in
-
- print("轨迹纠偏FailError: \(error ?? "")")
-
- // 纠偏失败添加原来的折线
- if let roadList = self.roadList {
-
- var commonPolylineCoords = [CLLocationCoordinate2D](repeating: CLLocationCoordinate2D(), count: roadList.count)
-
- for i in 0..<roadList.count {
- commonPolylineCoords[i].latitude = roadList[i].lat
- commonPolylineCoords[i].longitude = roadList[i].lng
- }
-
- // 构造折线对象
- if let commonPolyline = MAPolyline(coordinates: &commonPolylineCoords, count: UInt(roadList.count)) {
-
- self.roadLine = commonPolyline
-
- // 在地图上添加折线对象
- self.roadMapView.add(self.roadLine)
-
- //调用划线后,调用下面方法,设置地图可视矩形的范围
- self.roadMapView.setVisibleMapRect(self.roadLine.boundingMapRect, edgePadding: UIEdgeInsets(top: 40, left: 40, bottom: 40, right: 40), animated: true)
- }
-
- // 添加起点图标
- if let startTrack = roadList.first {
- let startPoint = MAPointAnnotation()
- startPoint.coordinate = CLLocationCoordinate2D(latitude: startTrack.lat, longitude: startTrack.lng)
- startPoint.title = "起点"
- startPoint.subtitle = "road_begin_icon"
- self.beginAnnotation = startPoint
- }
-
- // 添加终点图标
- if let endTrack = roadList.last {
- let endPoint = MAPointAnnotation()
- endPoint.coordinate = CLLocationCoordinate2D(latitude: endTrack.lat, longitude: endTrack.lng)
- endPoint.title = "终点"
- endPoint.subtitle = "road_end_icon"
- self.endAnnotation = endPoint
- }
-
- // 设置地址
- if let startTrack = roadList.first, let endTrack = roadList.last {
- self.mainView.startAddressLabel.text = "起点:\(startTrack.addr)"
- self.mainView.endAddressLabel.text = "终点:\(endTrack.addr)"
- }
-
- // 添加起点和终点注释
- if let startPoint = self.beginAnnotation,
- let endPoint = self.endAnnotation {
- self.roadMapView.addAnnotation(startPoint)
- self.roadMapView.addAnnotation(endPoint)
- }
- }
- }
- }
- }
- extension QSLRoadController: QSLRoadMainViewDelegate {
-
- func searchClickAction() {
-
- gravityInstance?.track(QSLGravityConst.road_search)
- if QSLBaseManager.shared.isVip() {
- //点击搜索埋点
- gravityInstance?.track(QSLGravityConst.vip_location_click_queryTrack, properties: ["id": 03008])
- } else {
- //点击搜索埋点
- gravityInstance?.track(QSLGravityConst.location_click_queryTrack, properties: ["id": 03009])
- }
- self.requestRoad()
- }
-
- // 开始时间点击
- func startTimeClickAction() {
-
- self.startDatePicker.selectDate = self.startDate
- self.startDatePicker.show()
- self.startDatePicker.resultBlock = { [weak self] date, value in
- gravityInstance?.track(QSLGravityConst.road_time_confirm)
- self?.mainView.startTimeLabel.text = "开始时间:\(value ?? "")"
-
- if let date = date {
- self?.startDate = date
- // 获取目标时间(加24小时)
- var nextDay = date.addingTimeInterval(24 * 60 * 60)
- // 获取时间戳
- let startTime = date.timeIntervalSince1970
- let nowTime = self?.nowDate.timeIntervalSince1970
- // 判断时间差是否小于24小时
- if (nowTime ?? 0) - startTime < 24 * 60 * 60 {
- nextDay = self?.nowDate ?? Date() // 当前时间
- }
-
- self?.mainView.endTimeLabel.text = nextDay.toformatterTimeString(formatter: "结束时间:yyyy-MM-dd HH:mm")
- self?.endDate = nextDay
- self?.endDatePicker.selectDate = self?.endDate
- }
- }
-
- self.startDatePicker.cancelBlock = {
-
- gravityInstance?.track(QSLGravityConst.road_time_cancel)
- }
- }
-
- // 结束时间点击
- func endTimeClickAction() {
-
- self.endDatePicker.selectDate = self.endDate
- self.endDatePicker.show()
- self.endDatePicker.resultBlock = { [weak self] date, value in
- gravityInstance?.track(QSLGravityConst.road_time_confirm)
- self?.mainView.endTimeLabel.text = "结束时间:\(value ?? "")"
-
- if let date = date {
-
- self?.endDate = date
- }
- }
-
- self.endDatePicker.cancelBlock = {
-
- gravityInstance?.track(QSLGravityConst.road_time_cancel)
- }
- }
- }
- extension QSLRoadController {
-
- func initView() {
-
- self.view.addSubview(roadMapView)
- roadMapView.snp.makeConstraints { make in
- make.top.right.left.equalTo(0)
- make.edges.equalToSuperview()
- }
-
- self.view.addSubview(mainView)
- mainView.snp.makeConstraints { make in
- make.left.right.bottom.equalTo(0)
- make.height.equalTo(369.rpx)
- }
-
- self.view.addSubview(foldBtn)
- foldBtn.snp.makeConstraints { make in
- make.size.equalTo(CGSize(width: 20.rpx, height: 4.rpx))
- make.centerX.equalToSuperview()
- make.bottom.equalTo(mainView.snp.top).offset(-6.rpx)
- }
-
- self.view.addSubview(backButton)
- backButton.snp.makeConstraints { make in
- make.size.equalTo(CGSize(width: 50.rpx, height: 50.rpx))
- make.left.equalTo(0)
- make.top.equalTo(QSLConst.qsl_kStatusBarFrameH)
- }
- }
- }
|