1. 程式人生 > >swift 百度地圖載入與百度地圖電子圍欄載入

swift 百度地圖載入與百度地圖電子圍欄載入

最近在寫百度地圖電子圍欄載入,研究原始碼,也花了一些時間。

1、百度電子圍欄整體思路

    1)使用者創造實體,賦予監控許可權

    2)對該實體創造電子圍欄,有服務端和本地端兩種方式,先創造實體,上傳到的伺服器中。在對實體創造電子圍欄,伺服器端的有多邊形,圓形,本地端的只有圓形圍欄。

    3)查詢對於該實體的電子圍欄操作,從代理返回值中在地圖上畫出圍欄。

    4)檢測實體運動軌跡,若超出範圍,則報警。(建立實體,允許獲得使用者軌跡,百度後臺返回的代理)

一、工程配置

    按照官網提示的即可,swift要加橋接而已。

二、

0、呼叫百度地圖服務之前,都需要設定ak mcode資訊,否則呼叫不到

let sop: BTKServiceOption = BTKServiceOption(ak: "ksqN5xi5s2Kpc6jlqW6Km5zk524pDhmy", mcode: "www.arvin.com.baiduMap", serviceID: 200447, keepAlive: false)
        BTKAction.sharedInstance().initInfo(sop) 

ak 是在百度地圖申請的,mcode是工程的Bundle id。

1、新增百度地圖,因為我們用鷹眼圍欄是需要在地圖上畫出圖形來的。

 _mapView = BMKMapView(frame: CGRect(x: 0, y: 0, width: self.view.frame.width, height: self.view.frame.height))

        self.view.addSubview(_mapView!)

按照百度地圖官網或者其demo即可。

2、新增百度地圖的第一步是首先需要建立實體,並給實體建立電子圍欄,上傳給百度地圖伺服器

    //設定地理圍欄
    func creatMapFrence() {
        //建立entity實體 entityDesc 字母數字下劃線
        let request: BTKAddEntityRequest = BTKAddEntityRequest(entityName: "entityA", entityDesc: "ThissaentityA", columnKey: nil, serviceID: 00000, tag: 1)
        BTKEntityAction.sharedInstance().addEntity(with: request, delegate: self)
         //建立一個名稱為“server_circle_fence” 的服務端圓形地理圍欄,圓心座標為東經116.3134度、北緯40.0478度,圍欄半徑為50米。它的監控物件為“entityA”,且當entityA這個終端實體的定位精度大於50米的軌跡點不參與此圍欄的計算。
        //圓心40.055573298959558)Optional(116.30384089417596
        let center: CLLocationCoordinate2D = CLLocationCoordinate2D.init(latitude: 40.055573298959558, longitude: 116.30384089417596)
        //構造將要建立的新的圍欄物件
        let fence: BTKServerCircleFence = BTKServerCircleFence(center: center, radius: 450.0, coordType: .COORDTYPE_BD09LL, denoiseAccuracy: 50, fenceName: "firstFenceName", monitoredObject: "entityA")
        //構建請求物件
        let circleRequest: BTKCreateServerFenceRequest = BTKCreateServerFenceRequest.init(serverCircleFence: fence, serviceID: 00000, tag: 1)
        //發起請求
        BTKFenceAction.sharedInstance().createServerFence(with: circleRequest, delegate: self)
      
    }

完成之後,我們需要呼叫建立實體是否成功的代理,判斷是否建立成功

 //建立地理圍欄返回代理
    func onCreateServerFence(_ response: Data!) {
        guard let array:[String: Any] = try! JSONSerialization.jsonObject(with: response, options: []) as? [String: Any] else {//轉化失敗就返回
            return
        }
        print("建立地理圍欄\(array)")
    }

2、搜尋我們可以在鷹眼代理BTKFenceDelegate 中查詢我們所建立的實體

//查詢圍欄
    func qureyMapFernce() {
        //構建請求物件
        let request = BTKQueryServerFenceRequest(monitoredObject: "entityA", fenceIDs: nil, outputCoordType: .COORDTYPE_BD09LL, serviceID: 000000, tag: 1)
        //傳送查詢請求
        BTKFenceAction.sharedInstance().queryServerFence(with: request, delegate: self)
        
    }

查詢地理圍欄並且在地圖上畫出地理圍欄

    func onQueryServerFence(_ response: Data!) {
        guard let array:[String: Any] = try! JSONSerialization.jsonObject(with: response, options: []) as? [String: Any] else {//轉化失敗就返回
            return
        }
        print("查詢地理圍欄\(array)")
        let size: NSInteger = array["size"] as! NSInteger
        //在地圖上展示這些圍欄
        //獲取size 在地理位置資訊
        //使用Annotation代表圓形圍欄的圓心
        let centerAnnotations: NSMutableArray = NSMutableArray(capacity: size)
        //使用填充圓行圍欄的覆蓋物
        let radiusOverlays: NSMutableArray = NSMutableArray(capacity: size)
        //儲存所有圍欄的圓心位置,是為了確定地圖的顯示範圍
        let coordinates: NSMutableArray = NSMutableArray(capacity: size)
        let dicFence: [[String: Any]] = array["fences"] as! [[String : Any]]
        
        for fence in dicFence {
            //篩選circle的圓
            let circle: String = fence["shape"] as! String
            if circle == "circle" {
                //解析資料 經緯度
                let fenceCenter: CLLocationCoordinate2D = CLLocationCoordinate2D(latitude: fence["latitude"] as! CLLocationDegrees, longitude: fence["longitude"] as! CLLocationDegrees)
                
                let fenceRadius: Double = fence["radius"] as! Double
                let fenceName: String = fence["fence_name"] as! String
                let denoiseAccuracy: NSInteger = fence["denoise"] as! NSInteger
                let monitoredObject: String = fence["monitored_person"] as! String
                
                //存貯圓心位置 /*略有問題*/
//                let coordinateValue: NSValue = NSValue.init(bytes: &fenceCenter, objCType: "CLLocationCoordinate2D")
                
                coordinates.add(fenceCenter)
                
                //構造Annotaion
                let annotation: BMKPointAnnotation = BMKPointAnnotation()
                annotation.coordinate = fenceCenter
                annotation.title = "預設名稱\(fenceName)"
                annotation.subtitle = "半徑:\(fenceRadius),去噪精度 \(denoiseAccuracy)"
                centerAnnotations.add(annotation)  //圓心陣列
                
                //圍欄的覆蓋範圍
                let coverageArea: BMKCircle = BMKCircle()
                coverageArea.coordinate = fenceCenter
                coverageArea.radius = fenceRadius
                
                radiusOverlays.add(coverageArea)
                
                
//                let annoationKey: NSValue = NSValue.value(annotation, withObjCType: <#UnsafePointer<Int8>#>)
                let annoationKey: NSValue = NSValue.init(nonretainedObject: annotation)  //轉換NSDate
                
                //儲存標註到圍欄的對映
                let fenceObject: BTKServerCircleFence = BTKServerCircleFence(center: fenceCenter, radius: fenceRadius, coordType: .COORDTYPE_BD09LL, denoiseAccuracy: UInt(denoiseAccuracy), fenceName: fenceName, monitoredObject: monitoredObject)
                annotationMapToFenceObject.setObject(fenceObject, forKey: annoationKey)
                
                //儲存標註到圍欄ID的對映
                let fenceID: NSNumber = fence["fence_id"] as! NSNumber
                annotationMapToFenceID.setObject(fenceID, forKey: annoationKey)

            } else {
                
            }
        }
//        weak let weakSelf = self
        //在地圖上展示圍欄 //主佇列 非同步
        DispatchQueue.main.async {
            //清空原有的標註和覆蓋物
            self._mapView?.removeOverlays(self._mapView?.overlays)
            self._mapView?.removeAnnotations(self._mapView?.annotations)
            //新增新的標註物和覆蓋物
            self._mapView?.addAnnotations(centerAnnotations as! [Any])
            self._mapView?.addOverlays(radiusOverlays as! [Any])
            //設定地圖的顯示範圍
            self.mapViewFitForCoordinates(points: coordinates)
        }
        
    }
//設定地圖的顯示範圍
    func mapViewFitForCoordinates(points: NSArray) {
        var minLat: Double = 90.0
        var maxLat: Double = -90.0
        var minLon: Double = 180.0
        var maxLon: Double = -180.0
        for i in 0..<points.count {
            let coord: CLLocationCoordinate2D = points[i] as! CLLocationCoordinate2D
            minLat = fmin(minLat, coord.latitude)
            maxLat = fmax(maxLat, coord.latitude)
            minLon = fmin(minLon, coord.longitude)
            maxLon = fmax(maxLon, coord.longitude)
        }
        
        let center: CLLocationCoordinate2D = CLLocationCoordinate2DMake((minLat + maxLat) * 0.5, (minLon + maxLon) * 0.5)
        //設定經緯度範圍
        let span = BMKCoordinateSpan(latitudeDelta: (maxLat - minLat) + 0.06, longitudeDelta: (maxLon - minLon) + 0.06)
        
        var region = BMKCoordinateRegion()
        region.center = center
        region.span = span
        
        self._mapView?.setRegion(region, animated: true)
    }
    
    
    //圍欄的畫圖,需要實現這個代理,否則畫不出來
    func mapView(_ mapView: BMKMapView!, viewFor overlay: BMKOverlay!) -> BMKOverlayView! {
        //判斷是圓形
        if overlay is BMKCircle {
            let circleView = BMKCircleView.init(overlay: overlay)
            circleView?.fillColor = UIColor.init(red: 0, green: 0, blue: 1, alpha: 0.3)
            circleView?.strokeColor = UIColor.init(red: 0, green: 0, blue: 1, alpha: 0)
            return circleView
        } else {
            return nil
        }
        
        
        
    }

刪除地理圍欄

//刪除圍欄
    func deleteMapFrence() {
        //請求構造物件
        let request: BTKDeleteServerFenceRequest = BTKDeleteServerFenceRequest(monitoredObject: "entityA", fenceIDs: nil, serviceID: 200447, tag: 22)
        //發起刪除請求
        BTKFenceAction.sharedInstance().deleteServerFence(with: request, delegate: self)
    }

刪除地理圍欄代理

 //刪除圍欄
    func onDeleteServerFence(_ response: Data!) {
        guard let array:[String: Any] = try! JSONSerialization.jsonObject(with: response, options: []) as? [String: Any] else {//轉化失敗就返回
            return
        }
        print("刪除地理圍欄\(array)")
    }

更新地理圍欄

//更新圍欄
    func updateMapFrence() {
        //新的圓心
        let center: CLLocationCoordinate2D = CLLocationCoordinate2DMake(40.1578, 116.2234)
        //新的圓形圍欄
        let fence: BTKServerCircleFence = BTKServerCircleFence(center: center, radius: 60, coordType: .COORDTYPE_BD09LL, denoiseAccuracy: 60, fenceName: "server_fence_60", monitoredObject: "entityB")
        //構建請求物件
        let request: BTKUpdateServerFenceRequest = BTKUpdateServerFenceRequest(serverCircleFence: fence, fenceID: 138, serviceID: 200447, tag: 1)
        //發起更新請求
        BTKFenceAction.sharedInstance().updateServerFence(with: request, delegate: self)

    }

實時查詢

    //實時狀態查詢
    func querServerFenceStatus() {
        //實時狀態查詢
        //鷹眼iOS SDK支援查詢指定監控物件的狀態,監控物件即某個終端實體(,,),監控物件是指其相對其上的地理圍欄的位置關係,是在圓形或
        //多邊形圍欄的內部還是外部,是否偏離了線性圍欄等。
        let request: BTKQueryServerFenceStatusRequest = BTKQueryServerFenceStatusRequest(monitoredObject: "entityA", fenceIDs: nil, serviceID: 200447, tag: 25)
        //發起查詢請求
        BTKFenceAction.sharedInstance().queryServerFenceStatus(with: request, delegate: self)
        
//        以下程式碼片段表示,假設“entityA”這個終端實體在東經120.44度,北緯40.11度的話,該終端實體和其上的fenceID為17、23、29的這幾個服務端地理圍欄的位置關係,只有這幾個地理圍欄的監控物件是“entityA”這個終端實體時才有意義,如果不知道有哪些地理圍欄在監控“entityA”這個終端實體,則fenceIDs屬性傳入nil即可。
        let customLocation: CLLocationCoordinate2D = CLLocationCoordinate2DMake(40.11, 120.44)
        //地理圍欄ID列表
//        NSArray *fenceIDs = ["",""]
        //構建請求物件
        let requestB: BTKQueryServerFenceStatusByCustomLocationRequest = BTKQueryServerFenceStatusByCustomLocationRequest(monitoredObject: "entityA", customLocation: customLocation, coordType: .COORDTYPE_BD09LL, fenceIDs: nil, serviceID: 200447, tag: 28)
        //發起查詢請求
        BTKFenceAction.sharedInstance().queryServerFenceStatusByCustomLocation(with: requestB, delegate: self)
        
    }

若是獲取報警資訊,我們應用的是BTKTraceDelegate的代理

//報警推送
    //當伺服器圍欄被觸發之後,會通過長連結將報警資訊推送給SDK,SDK會通過BTKActionDelete SDK會通過 BTKActionDelegate 協議的 -(void)onGetPushMessage:(BTKPushMessage *)message; 方法將報警資訊推送給開發者。因此服務端圍欄的報警推送要求網路暢通。當接收報警的手機斷網或網路狀態不好時,會導致報警推送失敗,鷹眼服務端將在後續的10分鐘之內每隔15s推送一次,直至收到成功響應。若10分鐘之後仍未成功,將不再推送,但報警記錄將儲存在鷹眼服務端。為避免因此造成報警漏接收,開發者可定期使用歷史報警查詢介面同步報警資訊。
    /*BTKTraceDelegate*/ //需要定義實體檢測其軌跡
    func onGet(_ message: BTKPushMessage!) {
        //獲得推送訊息
        if (message.type != 0x03 && message.type != 0x04) {
            return;
        }
        let content: BTKPushMessageFenceAlarmContent = message.content as! BTKPushMessageFenceAlarmContent
        var fenceName: String = "「" + content.fenceName + "」"
        let monitoredObject = "「" + content.monitoredObject + "」"
        var action = ""
        if content.actionType == .FENCE_MONITORED_OBJECT_ACTION_TYPE_ENTER {
            action = "進入"
        } else {
            action = "離開"
        }
        let fenceType = ""
        if message.type == 0x03 {
            fenceName = "伺服器圍欄"
        } else {
            fenceName = "客戶端圍欄"
        }
        
        //通過觸發報警的軌跡點,解析出觸發報警的時間
        let currentPoint: BTKFenceAlarmLocationPoint = content.currentPoint
        let dataFormatter: DateFormatter = DateFormatter()
        dataFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
        let alarmDate: Date = Date(timeIntervalSince1970: TimeInterval(currentPoint.loctime))
        let alarmDateStr: String = dataFormatter.string(from: alarmDate)
        
        let puchMessage: String = "終端" + monitoredObject + "在" + alarmDateStr + action + fenceType + fenceName
        //
        print("推送訊息\(puchMessage)")
        
        let deviceType: String = UIDevice.current.systemVersion
        let deviceTypeDouble: Double = Double(deviceType)!
        
        if deviceTypeDouble >= 10.0 {
            // 傳送本地通知UNNotificationRequest
            let notificationContent: UNMutableNotificationContent = UNMutableNotificationContent()
            notificationContent.title = "報警" + fenceType
            notificationContent.body = puchMessage
            notificationContent.sound = UNNotificationSound.default()
            let notificationTrigger: UNTimeIntervalNotificationTrigger = UNTimeIntervalNotificationTrigger(timeInterval: 0.1, repeats: false)
            let idd = "YYPushMessageNotificationIdentifier" + puchMessage
            
            let request: UNNotificationRequest = UNNotificationRequest(identifier: idd, content: notificationContent, trigger: notificationTrigger)
            UNUserNotificationCenter.current().add(request) { (error) in
                let errorMessage: NSError = error! as NSError
                if errorMessage.description == "" {
                    print("地理圍欄報警傳送失敗" + error.debugDescription)
                } else {
                    print("訊息傳送成功")
                    UIApplication.shared.applicationIconBadgeNumber += 1
                }
            }
            
            
            
        } else {
            
            
        }
        
        
    }

希望能幫到你們。