package com.cr.pages

import android.graphics.Bitmap
import android.graphics.Color
import android.graphics.drawable.BitmapDrawable
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.activityViewModels
import com.cr.common.*
import com.cr.cruav.CrApplication
import com.cr.cruav.R
import com.cr.data.CrConfig
import com.cr.data.CrUtil
import com.cr.dialog.DialogInput
import com.cr.dialog.DialogNormal
import com.cr.event.EventMapCapture
import com.cr.map.*
import com.cr.models.CompletionModel
import com.cr.models.ICompletion
import com.cr.viewmodel.CrFlightControlVM
import com.esri.arcgisruntime.arcgisservices.LabelDefinition
import com.esri.arcgisruntime.data.*
import com.esri.arcgisruntime.geometry.*
import com.esri.arcgisruntime.layers.ArcGISMapImageLayer
import com.esri.arcgisruntime.layers.ArcGISMapImageSublayer
import com.esri.arcgisruntime.layers.ArcGISTiledLayer
import com.esri.arcgisruntime.layers.FeatureLayer
import com.esri.arcgisruntime.layers.Layer
import com.esri.arcgisruntime.loadable.LoadStatus
import com.esri.arcgisruntime.mapping.ArcGISMap
import com.esri.arcgisruntime.mapping.view.*
import com.esri.arcgisruntime.symbology.*
import com.google.gson.JsonObject
import com.google.gson.JsonParser
import com.google.gson.JsonPrimitive
import com.squareup.otto.Subscribe
import kotlinx.android.synthetic.main.frag_map.*
import kotlinx.coroutines.*
import java.io.File
import java.io.FileOutputStream
import java.lang.Runnable
import java.util.concurrent.ExecutionException
import kotlin.collections.ArrayList
import kotlin.math.abs

/**
 * 操作系统：MAC系统
 * 创建者：王成
 * 创建日期：2023/3/14 09:51
 * 描述：地图界面
 */
class FragmentMap : CrAnimationFragment() {
    // todo: 2023/8/17 飞行器模型
    private val flightControlVm: CrFlightControlVM by activityViewModels()

    /**
     * 事件监听接口
     */
    interface EventListener {
        // todo: 2023/6/15 案件上传监听
        fun onCaseUpdate(selCase: CaseModel)
    }

    // define: 2023/3/14 地图容器
    private var mapView: MapView? = null

    // define: 2023/3/14 地图
    private var mMap: ArcGISMap? = null

    // todo: 2023/4/13 常量定义
    val ONLINE_LAYER_NAME = "底图"
    val ONLINE_LABEL_LAYER_NAME = "标签"

    // todo: 2023/4/13 图层名称定义
    val LAYER_NAME_ICO = "永久标志"
    val LAYER_NAME_DOODLE = "永久涂鸦"
    val LAYER_NAME_MEDIA = "案件点"
    val LAYER_NAME_CASE = "绘制案件图斑"
    val LAYER_NAME_YX = "影像图"
    val LAYER_NAME_GH = "规划图"
    val LAYER_NAME_XZ = "现状图"
    val LAYER_NAME_NET_CASE = "在线案件图斑"
    val LAYER_NAME_TEMP_ICO = "临时标志"
    val LAYER_NAME_TEMP_DOODLE = "临时涂鸦"
    val LAYER_NAME_CONFIG_VILLAGE = "行政区(村界)"
    val LAYER_NAME_CONFIG_TOWNS = "行政区(镇界)"

    val LAYER_NAME_AIR_LINE = "飞行航线"
    val LAYER_NAME_AIR_HISTORY_LINE = "历史航线"
    val LAYER_NAME_AIR_LOCATION = "飞行器"
    val LAYER_NAME_AIR_HOME = "返航点"

    // todo: 2023/4/14 符号相关常量
    val SYMBOL_WIDTH = 25.0f  //  define: 2023/4/13 图片符号的宽度
    val SYMBOL_HEIGHT = 25.0f  //  define: 2023/4/13 图片符号的高度
    val R_LABEL_WJ_NO = "wj_no"  //  define: 2023/4/13 未上传违建标识
    val R_LABEL_WJ_YES = "wj_yes" //  define: 2023/4/13 已上传违建标识
    val R_LABEL_REPEAT_NO = "repeat_no"  //  define: 2023/4/13 为上传复飞点标识
    val R_LABEL_REPEAT_YES = "repeat_yes" //  define: 2023/4/13 已上传复飞点标识

    // todo: 2023/4/14 媒体点相关常量
    val MEDIA_TYPE_WJ_NO = "0"    //  define: 2023/4/13 未上传的违建点
    val MEDIA_TYPE_WJ_YES = "1"  //  define: 2023/4/13 已上传的违建点
    val MEDIA_TYPE_REPEAT_NO = "4"   //  define: 2023/4/13 未上传的复飞案件
    val MEDIA_TYPE_REPEAT_YES = "5"   //  define: 2023/4/13 已上传的复飞案件

    // todo: 2023/4/14 字段相关常量
    companion object {
        const val FIELD_NET_CASE_POLYGON_AJH = "AJH"   //  define: 2023/4/13 网络案件的案件编号
        const val FIELD_CASE_POLYGON_ANJID = "ANJID"   //  define: 2023/4/13 案件ID
        const val FIELD_CASE_POLYGON_MJ = "MJ"   //  define: 2023/4/13 面积
        const val FIELD_CASE_POLYGON_BZ = "BZ"   //  define: 2023/4/13 违建面的类型 0--代表未匹配 1--代表已匹配
        const val FIELD_CASE_POLYGON_BZ_VALUE_NO = "0"   //  define: 2023/4/13 未匹配
        const val FIELD_CASE_POLYGON_BZ_VALUE_YES = "1"   //  define: 2023/4/13 已匹配
        const val FIELD_CASE_NAME = "NAME"   //  define: 2023/4/13 名称字段
        const val FIELD_CASE_IMAGES = "IMAGES"   //  define: 2023/4/13 图片集合字段
        const val FIELD_CASE_RQ = "RQ"   //  define: 2023/4/13 日期字段
        const val FIELD_CASE_LAT = "LAT"   //  define: 2023/4/13 纬度字段
        const val FIELD_CASE_LNG = "LNG"   //  define: 2023/4/13 经度字段
        const val FIELD_CASE_ISUP = "ISUP"   //  define: 2023/4/13 是否上传
        const val FIELD_CASE_ALT = "ALT"  //  define: 2023/4/13 高度字段
        const val FIELD_CASE_ANG = "ANG"   //  define: 2023/4/13 角度字段
        const val FIELD_CASE_TYPE = "TYPE"   //  define: 2023/4/13 类型字段
        const val FIELD_CASE_ISDOWN = "ISDOWN"   //  define: 2023/4/13 是否下载

        const val MEDIA_TYPE_WJ_NO = "0"  // define: 2023/6/12 未上传的案件点
        const val MEDIA_TYPE_WJ_YES = "1" // define: 2023/6/12 已上传的案件点
        const val MEDIA_TYPE_REPEAT_NO = "4" // define: 2023/6/12 未上传的复飞点
        const val MEDIA_TYPE_REPEAT_YES = "5" // define: 2023/6/12 已上传的复飞点
    }

    // todo: 2023/4/13 标志相关
    private var markChange: EventMarkChange? = null

    // todo: 2023/4/14 绘制图层
    private var gLayerIco: GraphicsOverlay? = null // define: 2023/4/13 标志图层
    private var gLayerAirplaneLocation: GraphicsOverlay? = null // define: 2023/4/13 飞行器位置图层
    private var gLayerAirplaneHomeLocation: GraphicsOverlay? = null // define: 2023/4/13 飞行器返航点位置图层
    private var gLayerAirplaneLine: GraphicsOverlay? = null // define: 2023/4/13 飞行航线图层
    private var gLayerHistoryAirplaneLine: GraphicsOverlay? = null // define: 2023/4/13 历史航线图层
    private var gLayerTemp: GraphicsOverlay? = null  // define: 2023/6/5 临时展示内容图层

    // todo: 2023/4/13 样式相关
    private var symbolAirplaneLine: SimpleLineSymbol? = null // define: 2023/4/13 飞行航线样式
    private var symbolAirplaneLink: SimpleLineSymbol? = null // define: 2023/4/13 飞行器与返航点连接线样式
    private var symbolAirplaneLocation: PictureMarkerSymbol? = null // define: 2023/4/13 飞行器符号样式

    // todo: 2023/4/13 要素相关
    private var builderAirplaneLine: PolylineBuilder? = null // define: 2023/4/13 航线点集
    private var builderAirplaneLink: PolylineBuilder? = null // define: 2023/4/13 飞行器与返航点连接线点集
    private var builderAirplaneLocation: PointBuilder? = null // define: 2023/4/13 飞行器位置点
    private var builderAirplaneHomeLocation: PointBuilder? = null // define: 2023/4/13 返航点位置
    private var graAirplaneLine: Graphic? = null // define: 2023/4/13 航线图形
    private var graAirplaneLink: Graphic? = null // define: 2023/4/13 飞行器与返航点连接线图形
    private var graAirplaneLocation: Graphic? = null // define: 2023/4/13 飞行器位置图形
    private var graAirplaneHomeLocation: Graphic? = null // define: 2023/4/13 返航点位置图形

    // todo: 2023/4/17 草图编辑
    private var sketchEditor: SketchEditor? = null // define: 2023/4/13 草图编辑器
    private var feaSelectCase: Feature? = null // define: 2023/4/13 选中的案件点

    // todo: 2023/4/14 编辑图层
    private var fTableMark: FeatureTable? = null // define: 2023/4/14 永久标志表
    private var fLayerMark: FeatureLayer? = null // define: 2023/4/14 永久标志图层
    private var fTableMedia: FeatureTable? = null // define: 2023/4/14 媒体表
    private var fLayerMedia: FeatureLayer? = null // define: 2023/4/14 媒体图层
    private var fTableDoodle: FeatureTable? = null // define: 2023/4/14 永久涂鸦表
    private var fLayerDoodle: FeatureLayer? = null // define: 2023/4/14 永久涂鸦图层
    private var fTableCasePolygon: FeatureTable? = null // define: 2023/4/14 永久案件表
    private var fLayerCasePolygon: FeatureLayer? = null // define: 2023/4/14 永久案件图层
    private var fTableNetCasePolygon: ServiceFeatureTable? = null // define: 2023/4/14 网路案件表
    private var fLayerNetCasePolygon: ArcGISMapImageSublayer? = null // define: 2023/4/14 网络案件图层

    // todo: 2023/4/18 地图Touch事件
    private var mapTouch: MapTouch? = null

    // todo: 2023/6/15 内部监听 自行设置
    private var eventListener: EventListener? = null

    // todo: 2023/8/8 截屏相关
    private var mapStateList = mutableListOf<MapStateModel>() // define: 2023/8/14 地图状态
    private var captureNames = mutableListOf<String>()  // define: 2023/8/14 截屏图片名称

    // todo: 2023/8/17 投影
    private val spatialReferenceWGS84 = SpatialReference.create(4326)
    private val spatialReferenceWebMTK = SpatialReference.create(3857)


    /**
     * 重写创建View方法
     * @param inflater LayoutInflater
     * @param container ViewGroup?
     * @param savedInstanceState Bundle?
     * @return View?
     */
    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        mainView = inflater.inflate(R.layout.frag_map, container, false)
        // todo: 2023/4/17 注册订阅
        CrApplication.getEventBus().register(this)
        return mainView
    }

    /**
     * 重写创建视图
     * @param view View
     * @param savedInstanceState Bundle?
     */
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        initUI()
    }

    /**
     * 初始化UI
     */
    private fun initUI() {
        //ArcGISRuntimeEnvironment.setLicense("runtimelite,1000,rud4449636536,none,NKMFA0PL4S0DRJE15166");
        // todo: 2023/3/14 初始化控件
        mapView = mainView?.findViewById(R.id.map_mapView)
        mapView?.let { itView ->
            // todo: 2023/4/13 设置底部标识是否显示
            itView.isAttributionTextVisible = false
            mMap = ArcGISMap()
            itView.map = mMap
            // todo: 2023/4/13 地图控件初始化
            itView.backgroundGrid.color = Color.argb(255, 216, 216, 216)
            itView.backgroundGrid.gridLineColor = Color.argb(0, 255, 255, 255)
            // todo: 2023/4/17 设置草图编辑
            sketchEditor = SketchEditor()
            setSketchEditor()
            itView.sketchEditor = sketchEditor
            // todo: 2023/4/18 Touch事件初始化
            mapTouch = MapTouch(CrApplication.getContext(), itView)
            mapTouch?.setListener(touchListener)
            itView.onTouchListener = mapTouch
            // todo: 2023/4/13 地图初始化
            mapInit()
        }

    }

    /**
     * 草图编辑工具监听
     */
    private var sketchGeometryChangeListener =
        SketchGeometryChangedListener { p0 ->
            if (p0!!.geometry.geometryType == GeometryType.POLYLINE) {
                // todo: 2023/6/5 清除已展示内容
                gLayerTemp?.graphics?.clear()
                var polyline = p0.geometry as Polyline
                var pointCollection = PointCollection(SpatialReference.create(3857))
                if (polyline.parts.size > 0) {
                    for (point in polyline.parts[0].points) {
                        pointCollection.add(point)
                        if (pointCollection.size >= 2) {
                            var polyline = Polyline(pointCollection)
                            // todo: 2023/6/5 计算长度
                            var lineLength = GeometryEngine.length(polyline)
                            // todo: 2023/6/5 添加标签
                            appendMeasureLabel(point, CrUnitManager.formatLength(lineLength))
                        }
                    }
                }
            } else if (p0!!.geometry.geometryType == GeometryType.POLYGON) {
                // todo: 2023/6/6 清除临时标签
                gLayerTemp?.graphics?.clear()
                var polygon = p0.geometry as Polygon
                if (polygon.parts.size > 0) {
                    if (CrUnitManager.querySizeByIterable(polygon.parts[0].points) >= 3) {
                        // todo: 2023/6/6 计算面积
                        var area = GeometryEngine.area(polygon)
                        area = abs(area)
                        // todo: 2023/6/6 添加标签
                        appendMeasureLabel(
                            polygon.parts[0].startPoint,
                            CrUnitManager.formatArea(area)
                        )
                    }
                }
            } else if (p0!!.geometry.geometryType == GeometryType.POINT && mapTouch!!.getAction() == MapAction.MapTapStopMoveWaypoint) {
                caseMoveWaypointToEnd(p0!!.geometry as Point)
            }
        }

    /**
     * 添加一个测量标签
     * @param location Point 位置
     * @param label String 标签内容
     */
    private fun appendMeasureLabel(location: Point, label: String) {
        var tSymbol = TextSymbol();
        tSymbol.color = Color.rgb(17, 46, 114)
        tSymbol.text = label
        tSymbol.size = 10f
        tSymbol.haloColor = Color.WHITE
        tSymbol.haloWidth = 2f
        tSymbol.fontWeight = TextSymbol.FontWeight.BOLD
        tSymbol.offsetY = 20f
        tSymbol.backgroundColor = Color.argb(200, 13, 49, 130)
        var graphic = Graphic(location, tSymbol)
        gLayerTemp?.graphics?.add(graphic)
    }

    /**
     * 设置草图编辑器
     */
    private fun setSketchEditor() {
        // todo: 2023/4/21 获取样式
        var sketchStyle: SketchStyle = sketchEditor!!.sketchStyle
        // todo: 2023/4/21 设置点符号样式
        var markerSymbol = SimpleMarkerSymbol()
        markerSymbol.size = 10f
        markerSymbol.color = Color.RED
        markerSymbol.style = SimpleMarkerSymbol.Style.CIRCLE

        // todo: 2023/4/21 设置样式
        sketchStyle.vertexSymbol = markerSymbol
        // todo: 2023/4/21 启用
        sketchEditor?.sketchStyle = sketchStyle

    }

    /**
     * Touch监听
     */
    private var touchListener = object : MapTouch.TouchListener {
        // todo: 2023/4/21 选择
        override fun onSelect(feature: Feature?, layer: Layer) {
            if (layer is FeatureLayer) {
                if (feature != null) {
                    // todo: 2023/4/18 高亮显示
                    focusFeature(layer!!, feature)
                } else {
                    unfocusFeature(layer!!)
                }
            }
        }

        // todo: 2023/4/19 标志添加回调
        override fun onMarkAppend(mapPoint: Point) {
            markAppend(mapPoint)
        }

        // todo: 2023/6/7 查询地理位置回调
        override fun onQueryLocation(location: Point, longitude: String, latitude: String) {
            appendLocation(location, longitude, latitude)
        }

        // todo: 2023/6/12 添加案件点
        override fun onAppendWaypoint(location: Point, longitude: String, latitude: String) {
            caseAppendWaypointToMap(location, longitude, latitude)
        }

        // todo: 2023/6/14 删除案件点
        override fun onRemoveWaypoint(screenPoint: android.graphics.Point) {
            caseRemoveCaseWaypoing(screenPoint)
        }

        // todo: 2023/6/14 选择移动的案件点
        override fun onMoveWaypointBySelect(location: Point) {
            caseMoveWaypointBySelect(location)
        }

        // todo: 2023/6/15 案件分享及上传选择回调
        override fun onCaseUploadBySelect(screenPoint: android.graphics.Point) {
            caseUpload(screenPoint)
        }

    }

    /**
     * 地图初始化
     */
    private fun mapInit() {
        // todo: 2023/4/13 加载在线底图 9月13日 注释 主要原因是加上后打不开地图
        //addOnlineRasterLayer()
        // todo: 2023/4/13 添加切片图层
        addTiledToMap()
        // todo: 2023/4/13 添加基础矢量地图
        addBaseGeoDatabaseToMap()
        // todo: 2023/4/14 添加可编辑矢量数据
        addEditGeoDatabaseToMap()
        // todo: 2023/8/25 加载在线网络案件
        addNetCaseLayerToMap()
        // todo: 2023/4/19 添加动态图层
        addGraphicOverlayToMap()
        // todo: 2023/8/17 添加飞行相关图层
        addAirplaneLayerToMap()
        // todo: 2023/4/13 定位地图中心点
        setMapCenter(118.709, 35.219, 5000.0);
    }

    /**
     * 添加在线地图
     */
    private fun addOnlineRasterLayer() {
        var url: String =
            "https://map.geoq.cn/ArcGIS/rest/services/ChinaOnlineStreetPurplishBlue/MapServer";
        var layer = ArcGISTiledLayer(url)
        layer.name = ONLINE_LAYER_NAME
        mMap?.operationalLayers?.add(layer)
    }

    /**
     * 添加切片图层
     */
    private fun addTiledToMap() {
        var tileLayers = DataManager.getTileLayerConfig()
        for (model in tileLayers) {
            var layer = ArcGISTiledLayer(model.url)
            layer.name = model.name
            // todo: 2023/4/13 添加到配置 同时获取可见性
            val isVisible = LayerManager.getInstance().addLayer(
                LayerModel(
                    model.name!!,
                    model.isVisible,
                    LayerType.LAYER_TYPE_TILED,
                    layer
                ), LayerManager.LayerGroup.LAYER_NAME_DAILY
            )
            layer.isVisible = isVisible
            mMap?.operationalLayers?.add(layer)
        }
    }

    /**
     * 添加基础矢量地图集合
     */
    private fun addBaseGeoDatabaseToMap() {
        if (!CrFileManager.isExists(CrUtil.MAP_PATH_BASE)) return
        var geoDatabase = Geodatabase(CrUtil.MAP_PATH_BASE)
        // todo: 2023/4/13 加载完成监听
        geoDatabase.addDoneLoadingListener(Runnable {
            var vLayers = DataManager.getVectorLayerConfig()
            var tables = geoDatabase.geodatabaseFeatureTables
            for (vLayer in vLayers) {
                var table = tables[vLayer.lyrIdx]
                var layer = FeatureLayer(table)
                layer.name = vLayer.lyrName
                // todo: 2023/4/13 添加到配置信息 同时获取可见性
                val isVisible = LayerManager.getInstance().addLayer(
                    LayerModel(
                        vLayer.lyrName,
                        vLayer.isVisible,
                        LayerType.LAYER_TYPE_FEATURE,
                        layer
                    ), LayerManager.LayerGroup.LAYER_NAME_BASE
                )
                layer.isVisible = isVisible
                mMap?.operationalLayers?.add(layer)
            }
        })
        // todo: 2023/4/13 加载数据集
        geoDatabase.loadAsync()
    }

    /**
     * 加载可编辑数据
     */
    private fun addEditGeoDatabaseToMap() {
        if (!CrFileManager.isExists(CrUtil.MAP_PATH_EDIT)) return
        var geoDatabase = Geodatabase(CrUtil.MAP_PATH_EDIT)
        // todo: 2023/4/14 数据加载完监听
        geoDatabase.addDoneLoadingListener(Runnable {
            // todo: 2023/4/14 初始化标志图层
            fTableMark = geoDatabase.getGeodatabaseFeatureTable("ico84")
            fLayerMark = FeatureLayer(fTableMark)
            fLayerMark!!.name = LAYER_NAME_ICO
            // todo: 2023/9/27 添加图层配置并获取默认可见性
            val isVisibleMark = LayerManager.getInstance().addLayer(
                LayerModel(
                    fLayerMark!!.name,
                    true, LayerType.LAYER_TYPE_FEATURE_EDIT, fLayerMark!!
                ), LayerManager.LayerGroup.LAYER_NAME_DAILY
            )
            fLayerMark!!.isVisible = isVisibleMark
            mMap!!.operationalLayers.add(fLayerMark)
            // todo: 2023/4/14  初始化涂鸦图层
            fTableDoodle = geoDatabase.getGeodatabaseFeatureTable("ty84")
            fLayerDoodle = FeatureLayer(fTableDoodle)
            fLayerDoodle!!.name = LAYER_NAME_DOODLE
            // todo: 2023/9/27 添加图层配置 并获取默认可见性
            val isVisibleDoodle = LayerManager.getInstance().addLayer(
                LayerModel(
                    fLayerDoodle!!.name,
                    true, LayerType.LAYER_TYPE_FEATURE_EDIT, fLayerDoodle!!
                ), LayerManager.LayerGroup.LAYER_NAME_DAILY
            )
            fLayerDoodle!!.isVisible = isVisibleDoodle
            mMap!!.operationalLayers.add(fLayerDoodle)
            // todo: 2023/4/14  初始化媒体点图层
            fTableMedia = geoDatabase.getGeodatabaseFeatureTable("media84")
            fLayerMedia = FeatureLayer(fTableMedia)
            fLayerMedia!!.name = LAYER_NAME_MEDIA
            // todo: 2023/9/27 添加图层配置 并获取默认可见性
            val isVisibleMedia = LayerManager.getInstance().addLayer(
                LayerModel(
                    fLayerMedia!!.name,
                    true, LayerType.LAYER_TYPE_FEATURE_EDIT, fLayerMedia!!
                ), LayerManager.LayerGroup.LAYER_NAME_DAILY
            )
            fLayerMedia!!.isVisible = isVisibleMedia
            mMap!!.operationalLayers.add(fLayerMedia)
            // todo: 2023/4/14  初始化违建面图层
            fTableCasePolygon = geoDatabase.getGeodatabaseFeatureTable("sbwj84")
            fLayerCasePolygon = FeatureLayer(fTableCasePolygon)
            fLayerCasePolygon!!.name = LAYER_NAME_CASE
            // todo: 2023/4/14 给图层添加标注
            val labelDefinition: LabelDefinition =
                createLabelDefinition(Color.BLUE, 12.0f, "round(\$feature.MJ,2) + '亩';")
            fLayerCasePolygon!!.labelDefinitions.add(labelDefinition)
            fLayerCasePolygon!!.isLabelsEnabled = true
            // todo: 2023/9/27 添加图层配置 并获取默认可见性
            val isVisibleCase = LayerManager.getInstance().addLayer(
                LayerModel(
                    fLayerCasePolygon!!.name,
                    true,
                    LayerType.LAYER_TYPE_FEATURE_EDIT,
                    fLayerCasePolygon!!
                ), LayerManager.LayerGroup.LAYER_NAME_DAILY
            )
            fLayerCasePolygon!!.isVisible = isVisibleCase
            mMap!!.operationalLayers.add(fLayerCasePolygon)
            // todo: 2023/4/14 可编辑数据符号化
            editLayerRenderer()
        })
        // todo: 2023/4/14 异步加载
        geoDatabase.loadAsync()
    }

    /**
     * 添加动态图层到地图
     */
    private fun addGraphicOverlayToMap() {
        // todo: 2023/4/19 初始化动态标志图层
        gLayerIco = GraphicsOverlay()
        appendGraphicOverlay(gLayerIco!!, LAYER_NAME_TEMP_ICO)

        // todo: 2023/6/5 初始化临时展示内容图层
        gLayerTemp = GraphicsOverlay();
        mapView?.graphicsOverlays?.add(gLayerTemp)

    }

    /**
     * 添加飞行相关图层到地图
     */
    private fun addAirplaneLayerToMap() {
        // todo: 2023/8/16 初始化航线图层
        gLayerAirplaneLine = GraphicsOverlay()
        gLayerAirplaneLine?.let {
            overlaysAddToMap(it, LAYER_NAME_AIR_LINE, LayerManager.LayerGroup.LAYER_NAME_AIR)
        }
        // todo: 2023/8/16 初始化历史航线图层
        gLayerHistoryAirplaneLine = GraphicsOverlay()
        gLayerHistoryAirplaneLine?.let {
            overlaysAddToMap(
                it,
                LAYER_NAME_AIR_HISTORY_LINE,
                LayerManager.LayerGroup.LAYER_NAME_AIR
            )
        }
        // todo: 2023/8/17 初始化返航点图层
        gLayerAirplaneHomeLocation = GraphicsOverlay()
        gLayerAirplaneHomeLocation?.let {
            overlaysAddToMap(it, LAYER_NAME_AIR_HOME, LayerManager.LayerGroup.LAYER_NAME_AIR)
        }
        // todo: 2023/8/17 初始化飞行器图层
        gLayerAirplaneLocation = GraphicsOverlay()
        gLayerAirplaneLocation?.let {
            overlaysAddToMap(it, LAYER_NAME_AIR_LOCATION, LayerManager.LayerGroup.LAYER_NAME_AIR)
        }
        // todo: 2023/8/17 符号化
        initAirplaneSymbolInfo()
    }

    /**
     * 加载网络案件图斑到地图
     */
    private fun addNetCaseLayerToMap() {
        // todo: 2023/8/25 创建地图服务
        var mapService = ArcGISMapImageLayer(CrConfig.user!!.netCaseServiceUrl!!)
        // todo: 2023/8/25 添加监听
        mapService.addDoneLoadingListener {
            if (mapService.loadStatus == LoadStatus.LOADED) {
                fLayerNetCasePolygon = mapService.sublayers[0] as ArcGISMapImageSublayer
                fLayerNetCasePolygon?.let {
                    // todo: 2023/8/25 案件图斑图层加载监听
                    it.addDoneLoadingListener {
                        if (it.loadStatus == LoadStatus.LOADED) {
                            fTableNetCasePolygon = it.table
                        }
                    }
                    // todo: 2023/8/25 加载 必须添加该方法 否则无法获取数据表
                    it.loadAsync()
                    // todo: 2023/8/25 设置
                    it.name = LAYER_NAME_NET_CASE
                    it.isVisible = true
                    it.isLabelsEnabled = false
                    LayerManager.getInstance().addLayer(
                        LayerModel(
                            it.name,
                            it.isVisible,
                            LayerType.LAYER_TYPE_SERVER,
                            it
                        ), LayerManager.LayerGroup.LAYER_NAME_DAILY
                    )
                }
            }
        }
        // todo: 2023/8/25 添加到地图中
        mMap?.operationalLayers?.add(mapService)
    }

    /**
     * 飞行相关符号化
     */
    private fun initAirplaneSymbolInfo() {
        // todo: 2023/8/17 符号化航线
        symbolAirplaneLine = SimpleLineSymbol(SimpleLineSymbol.Style.SOLID, Color.WHITE, 2.0f)
        symbolAirplaneLink = SimpleLineSymbol(SimpleLineSymbol.Style.SOLID, Color.GREEN, 2.0f)
        builderAirplaneLine = PolylineBuilder(spatialReferenceWGS84)
        builderAirplaneLink = PolylineBuilder(spatialReferenceWGS84)
        graAirplaneLine = Graphic(builderAirplaneLine!!.toGeometry(), symbolAirplaneLine)
        graAirplaneLink = Graphic(builderAirplaneLink!!.toGeometry(), symbolAirplaneLink)
        gLayerAirplaneLine?.graphics?.add(graAirplaneLine)
        gLayerAirplaneLine?.graphics?.add(graAirplaneLink)
        // todo: 2023/8/17 飞行器符号化
        builderAirplaneLocation = PointBuilder(spatialReferenceWGS84)
        symbolAirplaneLocation = createPictureMarkerSymbol(R.drawable.ico_air, 25f, 40f)
        graAirplaneLocation =
            Graphic(builderAirplaneLocation!!.toGeometry(), symbolAirplaneLocation)
        gLayerAirplaneLocation?.graphics?.add(graAirplaneLocation)
        // todo: 2023/8/17 返航位置初始化
        builderAirplaneHomeLocation = PointBuilder(spatialReferenceWGS84)
        var symbolAirplaneHomeLocation = createPictureMarkerSymbol(R.drawable.ico_home, 30f, 30f)
        graAirplaneHomeLocation =
            Graphic(builderAirplaneHomeLocation!!.toGeometry(), symbolAirplaneHomeLocation)
        gLayerAirplaneHomeLocation?.graphics?.add(graAirplaneHomeLocation)
    }

    /**
     * 添加绘制图层到地图中
     * @param layer GraphicsOverlay 绘制图层
     * @param layerName String 图层名称
     * @param group LayerGroup 分组
     */
    private fun overlaysAddToMap(
        layer: GraphicsOverlay,
        layerName: String,
        group: LayerManager.LayerGroup
    ) {
        // todo: 2023/8/17 加入到图层控制中
        val isVisible = LayerManager.getInstance().addLayer(
            LayerModel(
                layerName,
                true,
                LayerType.LAYER_TYPE_GRAPHIC,
                layer
            ), group
        )
        // todo: 2023/8/17 设置可见
        layer.isVisible = isVisible
        // todo: 2023/8/17 加入到地图中
        mapView?.graphicsOverlays!!.add(layer)
    }

    /**
     * 编辑图层符号化
     */
    private fun editLayerRenderer() {
        // todo: 2023/4/14  永久涂鸦图层符号化
        val doodleSymbol = SimpleLineSymbol(SimpleLineSymbol.Style.SOLID, Color.BLUE, 2.5f)
        val doodleRenderer = SimpleRenderer(doodleSymbol)
        fLayerDoodle?.renderer = doodleRenderer
        // todo: 2023/4/14  永久标志图层符号化
        val icoSymbol: PictureMarkerSymbol =
            createPictureMarkerSymbol(R.drawable.ico_ty1, 30f, 30f)
        val icoRenderer = SimpleRenderer(icoSymbol)
        fLayerMark?.renderer = icoRenderer
        // todo: 2023/4/14  违建符号化
        val mediaRenderer = UniqueValueRenderer()
        val mediaSymbolYes: PictureMarkerSymbol = createPictureMarkerSymbol(
            R.drawable.ico_wj_upload_yes,
            SYMBOL_WIDTH,
            SYMBOL_HEIGHT
        )
        val mediaSymbolNo: PictureMarkerSymbol = createPictureMarkerSymbol(
            R.drawable.ico_wj_no,
            SYMBOL_WIDTH,
            SYMBOL_HEIGHT
        )
        val mediaSymbolRepeatYes: PictureMarkerSymbol = createPictureMarkerSymbol(
            R.drawable.ico_repeatflight_yes,
            SYMBOL_WIDTH,
            SYMBOL_HEIGHT
        )
        val mediaSymbolRepeatNo: PictureMarkerSymbol = createPictureMarkerSymbol(
            R.drawable.ico_repeatflight_no,
            SYMBOL_WIDTH,
            SYMBOL_HEIGHT
        )
        mediaRenderer.defaultSymbol = mediaSymbolNo
        mediaRenderer.defaultLabel = R_LABEL_WJ_NO
        mediaRenderer.fieldNames.add(FIELD_CASE_ISUP)
        val cValueNo: MutableList<Any> = ArrayList()
        cValueNo.add(MEDIA_TYPE_WJ_NO)
        mediaRenderer.uniqueValues.add(
            UniqueValueRenderer.UniqueValue(
                R_LABEL_WJ_NO,
                R_LABEL_WJ_NO,
                mediaSymbolNo,
                cValueNo
            )
        )
        val cValueYes: MutableList<Any> = ArrayList()
        cValueYes.add(MEDIA_TYPE_WJ_YES)
        mediaRenderer.uniqueValues.add(
            UniqueValueRenderer.UniqueValue(
                R_LABEL_WJ_YES,
                R_LABEL_WJ_YES,
                mediaSymbolYes,
                cValueYes
            )
        )
        val cValueRepeatNo: MutableList<Any> = ArrayList()
        cValueRepeatNo.add(MEDIA_TYPE_REPEAT_NO)
        mediaRenderer.uniqueValues.add(
            UniqueValueRenderer.UniqueValue(
                R_LABEL_REPEAT_NO,
                R_LABEL_REPEAT_NO,
                mediaSymbolRepeatNo,
                cValueRepeatNo
            )
        )
        val cValueRepeatYes: List<Any> = ArrayList()
        cValueNo.add(MEDIA_TYPE_REPEAT_YES)
        mediaRenderer.uniqueValues.add(
            UniqueValueRenderer.UniqueValue(
                R_LABEL_REPEAT_YES,
                R_LABEL_REPEAT_YES,
                mediaSymbolRepeatYes,
                cValueRepeatYes
            )
        )
        fLayerMedia?.renderer = mediaRenderer
        // todo: 2023/4/14  绘制违建面符号化
        val colorClear = Color.argb(0, 255, 255, 255)
        val caseNoLineSymbol = SimpleLineSymbol(SimpleLineSymbol.Style.DASH_DOT, Color.BLUE, 2.5f)
        val caseYesLineSymbol = SimpleLineSymbol(SimpleLineSymbol.Style.DASH_DOT, Color.RED, 2.5f)
        val caseNoFillSymbol =
            SimpleFillSymbol(SimpleFillSymbol.Style.SOLID, colorClear, caseNoLineSymbol)
        val caseYesFillSymbol =
            SimpleFillSymbol(SimpleFillSymbol.Style.SOLID, colorClear, caseYesLineSymbol)
        val casePolygonRenderer = UniqueValueRenderer()
        casePolygonRenderer.fieldNames.add(FIELD_CASE_POLYGON_BZ)
        casePolygonRenderer.defaultSymbol = caseNoFillSymbol
        casePolygonRenderer.defaultLabel = FIELD_CASE_POLYGON_BZ_VALUE_NO
        val cValueCaseNo: MutableList<Any> = ArrayList()
        cValueCaseNo.add(FIELD_CASE_POLYGON_BZ_VALUE_NO)
        casePolygonRenderer.uniqueValues.add(
            UniqueValueRenderer.UniqueValue(
                FIELD_CASE_POLYGON_BZ_VALUE_NO,
                FIELD_CASE_POLYGON_BZ_VALUE_NO,
                caseNoFillSymbol,
                cValueCaseNo
            )
        )
        val cValueCaseYes: MutableList<Any> = ArrayList()
        cValueCaseYes.add(FIELD_CASE_POLYGON_BZ_VALUE_YES)
        casePolygonRenderer.uniqueValues.add(
            UniqueValueRenderer.UniqueValue(
                FIELD_CASE_POLYGON_BZ_VALUE_YES,
                FIELD_CASE_POLYGON_BZ_VALUE_YES,
                caseYesFillSymbol,
                cValueCaseYes
            )
        )
        fLayerCasePolygon?.renderer = casePolygonRenderer
    }

    /**
     * 向地图中添加动态绘制图层
     * @param layer GraphicsOverlay 动态绘制图层
     * @param layerName String 图层名称
     */
    private fun appendGraphicOverlay(layer: GraphicsOverlay, layerName: String) {
        // todo: 2023/4/19 设置该图层显示
        mapView?.graphicsOverlays?.let {
            // todo: 2023/9/27 添加图层配置 并获取默认可见性
            val isVisible = LayerManager.getInstance().addLayer(
                LayerModel(
                    layerName,
                    true,
                    LayerType.LAYER_TYPE_GRAPHIC,
                    layer
                ), LayerManager.LayerGroup.LAYER_NAME_DRAW
            )
            layer.isVisible = isVisible
            it.add(layer)
        }
    }

    /**
     * 创建图片符号
     * @param drawable Int 图片资源Id
     * @param width Float 宽度
     * @param height Float 高度
     * @return PictureMarkerSymbol?
     */
    private fun createPictureMarkerSymbol(
        drawable: Int,
        width: Float,
        height: Float
    ): PictureMarkerSymbol {
        val tDrawable = CrApplication.getContext().resources.getDrawable(drawable) as BitmapDrawable
        val symbol = PictureMarkerSymbol(tDrawable)
        if (width > 0) symbol.width = width else symbol.width =
            CrUnitManager.px2dp(tDrawable.bitmap.width).toFloat()
        if (height > 0) symbol.height = height else symbol.height =
            CrUnitManager.px2dp(tDrawable.bitmap.height).toFloat()
        return symbol
    }

    /**
     * 创建标注
     * @param txtColor Int 文字颜色
     * @param txtSize Float 文字大小
     * @param expression String 表达式
     * @return LabelDefinition? 标注
     */
    private fun createLabelDefinition(
        txtColor: Int,
        txtSize: Float,
        expression: String
    ): LabelDefinition {
        val textSymbol = TextSymbol()
        textSymbol.color = txtColor
        textSymbol.fontWeight = TextSymbol.FontWeight.BOLD
        textSymbol.haloColor = Color.WHITE
        textSymbol.haloWidth = 2.0f
        textSymbol.fontFamily = "Arial"
        textSymbol.size = txtSize
        val label = JsonObject()
        val labelExpression = JsonObject()
        labelExpression.add("expression", JsonPrimitive(expression))
        label.add("labelExpressionInfo", labelExpression)
        label.add("labelPlacement", JsonPrimitive("esriServerPolygonPlacementAlwaysHorizontal"))
        label.add("symbol", JsonParser().parse(textSymbol.toJson()))
        return LabelDefinition.fromJson(label.toString())
    }

    /**
     * 定位地图中心
     * @param longitude Double 中心经度
     * @param latitude Double 中心纬度
     * @param scale Double 缩放比例
     */
    private fun setMapCenter(longitude: Double, latitude: Double, scale: Double) {
        // todo: 2023/4/13 创建中心点
        var centerPoint: Point =
            Point(longitude, latitude, MapTouch.wgs84SpatialReference)
        if (scale > 0) {
            mapView?.setViewpointCenterAsync(centerPoint, scale)
        } else {
            mapView?.setViewpointCenterAsync(centerPoint)
        }
    }

    /**
     * 高亮显示要素
     * @param layer FeatureLayer 要素所在图层
     * @param feature Feature 要素
     */
    private fun focusFeature(layer: FeatureLayer, feature: Feature) {
        layer.selectFeature(feature)
    }

    /**
     * 清除高亮显示
     * @param layer FeatureLayer 图层
     */
    private fun unfocusFeature(layer: FeatureLayer) {
        layer.clearSelection()
    }

    /**
     * 清除全部选择
     */
    private fun unfocusAllFeature() {
        for (layer in mMap!!.operationalLayers) {
            if (layer is FeatureLayer) {
                layer.clearSelection()
            }
        }

        for (overLayer in mapView!!.graphicsOverlays) {
            overLayer.clearSelection()
        }
    }

    /**
     * 开始绘制涂鸦
     */
    private fun doodleStartDraw() {
        sketchEditor?.start(SketchCreationMode.FREEHAND_LINE)
    }

    /**
     * 清除草图
     */
    private fun clearSketch() {
        sketchEditor?.clearGeometry()
    }

    /**
     * 保存绘制的涂鸦
     */
    private fun doodleSave() {
        var geometry: Geometry? = sketchEditor?.geometry
        geometry?.let {
            var feature: Feature = fTableDoodle!!.createFeature()
            feature.geometry = it
            var addAsync = fTableDoodle!!.addFeatureAsync(feature)
            addAsync.addDoneListener(Runnable {
                try {
                    addAsync.get()
                    if (addAsync.isDone) {
                        showInformation("涂鸦保存成功！")
                        sketchEditor?.stop()
                        CrApplication.getEventBus()
                            .post(FragmentDoodle.EventDoodleAction(FragmentDoodle.EventDoodleAction.CLOSE_DRAW))
                    }
                } catch (e: InterruptedException) {
                    showWarning(e.message!!)
                } catch (e: ExecutionException) {
                    showWarning(e.message!!)
                }
            })
        }
    }

    /**
     * 删除数据表中的全部数据
     * @param delTable FeatureTable 数据表
     */
    private fun removeAllFeatureByTable(delTable: FeatureTable) {
        var dialog = DialogNormal(CrApplication.getContext(), "警告", "删除后无法恢复，是否删除？")
        dialog.setButtonsText("是", "否")
        dialog.setListener(object : DialogNormal.DialogNormalListener {
            // todo: 2023/4/17 删除
            override fun completion() {
                var params = QueryParameters()
                params.whereClause = "1=1"
                var queryAsync = delTable?.queryFeaturesAsync(params)
                queryAsync?.addDoneListener(Runnable {
                    try {
                        if (queryAsync.isDone) {
                            var deleteAsync = delTable?.deleteFeaturesAsync(queryAsync.get())
                            deleteAsync?.addDoneListener(Runnable {
                                try {
                                    if (deleteAsync.isDone) {
                                        showInformation("删除成功！")
                                    }
                                } catch (e: java.lang.IllegalArgumentException) {
                                    showError("删除错误！")
                                }
                            })
                        }

                    } catch (e: java.lang.IllegalArgumentException) {
                        showError("删除错误！")
                    }
                })
            }

            // todo: 2023/4/17 否
            override fun close() {

            }

        })
        dialog.show()
    }

    /**
     * 删除图层中选中的要素
     * @param layer FeatureLayer 图层
     */
    private fun doodleSelectFeature(layer: FeatureLayer) {
        var dialog = DialogNormal(requireContext(), "警告", "删除后无法恢复，是否删除？")
        dialog.setButtonsText("是", "否")
        dialog.setListener(object : DialogNormal.DialogNormalListener {
            override fun completion() {
                var sAsync = layer!!.selectedFeaturesAsync
                sAsync.addDoneListener(Runnable {
                    if (sAsync.isDone) {
                        var result = sAsync.get()
                        var dAsync = layer.featureTable.deleteFeaturesAsync(result)
                        dAsync.addDoneListener(Runnable {
                            try {
                                if (dAsync.isDone) {
                                    showInformation("删除成功！")
                                }
                            } catch (e: java.lang.IllegalArgumentException) {
                                showError("删除错误！")
                            }
                        })
                    }
                })
            }

            override fun close() {
            }
        })
        dialog.show()
    }

    /**
     * 添加标志
     * @param mapPoint Point 地图点
     */
    private fun markAppend(mapPoint: Point) {
        if (markChange == null) return
        var markSymbol = createPictureMarkerSymbol(markChange!!.markDrawable, 0f, 0f)
        // todo: 2023/4/19 设置偏移
        markSymbol.offsetX = -1 * markSymbol.width / 2
        markSymbol.angle = markChange!!.markAngle.toFloat()
        // todo: 2023/4/19 创建要素
        var markGraphic = Graphic(mapPoint, markSymbol)
        gLayerIco?.graphics?.add(markGraphic)
        CrUtil.showToast("标志添加成功！")
    }

    /**
     * 保存标志
     */
    private fun markSave() {
        gLayerIco?.let {
            var features: MutableList<Feature> = mutableListOf()
            for (graphic in it.graphics) {
                var feature = fTableMark?.createFeature()
                feature?.geometry = graphic.geometry
                features.add(feature!!)
            }
            var addAsync = fTableMark?.addFeaturesAsync(features)
            addAsync?.addDoneListener(Runnable {
                try {
                    if (addAsync.isDone) {
                        showInformation("保存成功！")
                        gLayerIco?.graphics?.clear()
                    }
                } catch (e: java.lang.IllegalArgumentException) {
                    showError("保存错误！")
                }
            })
        }
    }

    /**
     * 添加展示获取经纬度位置
     * @param location Point 地理位置
     * @param longitude String 经度
     * @param latitude String 纬度
     */
    private fun appendLocation(location: Point, longitude: String, latitude: String) {
        // todo: 2023/6/7 点符号样式
        var markSymbol: SimpleMarkerSymbol = SimpleMarkerSymbol()
        markSymbol.color = Color.argb(255, 0, 0, 255)
        markSymbol.size = 14.0f
        markSymbol.style = SimpleMarkerSymbol.Style.CIRCLE
        markSymbol.outline =
            SimpleLineSymbol(SimpleLineSymbol.Style.SOLID, Color.argb(255, 255, 255, 255), 1.0f)
        // todo: 2023/6/7 创建点符号
        var graphic = Graphic(location, markSymbol)
        gLayerTemp?.graphics?.add(graphic)
        // todo: 2023/6/7 添加标注
        appendMeasureLabel(location, String.format("经度:%s   纬度:%s", longitude, latitude))
    }

    /**
     * 添加经纬度点到地图中
     * @param longitude String 经度值
     * @param latitude String 纬度值
     */
    private fun appendWGS84PointToMap(longitude: String, latitude: String) {
        var lng = longitude.toDouble()
        var lat = latitude.toDouble()
        var wgs84Point = Point(lng, lat, SpatialReference.create(4326))
        var location = GeometryEngine.project(wgs84Point, mMap?.spatialReference) as Point
        appendLocation(location, longitude, latitude)
    }


    /**
     * 测量长度
     */
    private fun measureLength() {
        CrUtil.showToast("地图上点击开始测量！")
        sketchEditor?.let {
            it.stop()
            it.removeGeometryChangedListener(sketchGeometryChangeListener)
            // todo: 2023/4/21 设置编辑模式
            var model = SketchEditConfiguration()
            model.isAllowPartSelection = false
            model.isContextMenuEnabled = false
            model.isRequireSelectionBeforeDrag = true
            it.start(SketchCreationMode.POLYLINE, model)
            it.opacity = 1.0f
            // todo: 2023/4/21 设置监听
            it.addGeometryChangedListener(sketchGeometryChangeListener)
        }
    }

    /**
     * 面积测量
     */
    private fun measureArea() {
        CrUtil.showToast("地图上点击开始测量！")
        sketchEditor?.let {
            it.stop()
            it.removeGeometryChangedListener(sketchGeometryChangeListener)
            // todo: 2023/6/6 设置编辑模式
            var model = SketchEditConfiguration()
            model.isAllowPartSelection = false
            model.isContextMenuEnabled = false
            model.isRequireSelectionBeforeDrag = true
            it.start(SketchCreationMode.POLYGON, model)
            it.opacity = 1.0f
            // todo: 2023/6/6 设置监听
            it.addGeometryChangedListener(sketchGeometryChangeListener)
        }
    }

    /**
     * 输入坐标定位监听
     */
    private var inputLocationToMapListener: DialogInput.DialogInputListener =
        object : DialogInput.DialogInputListener {
            // todo: 2023/6/7 点击完成按钮回调
            override fun completion(valueOne: String, valueTwo: String, self: DialogInput) {
                if (!CrUnitManager.checkLongitude(valueOne)) {
                    CrUtil.showToast("输入的经度值不符合要求！")
                } else if (!CrUnitManager.checkLatitude(valueTwo)) {
                    CrUtil.showToast("输入的纬度值不符合要求！")
                } else {
                    self.dismiss()
                    // todo: 2023/6/8 添加到地图中
                    appendWGS84PointToMap(valueOne, valueTwo)
                }
            }

            // todo: 2023/6/7 点击取消按钮回调
            override fun close() {
            }
        }

    /**
     * 开始绘制案件图斑
     */
    private fun caseStartDraw() {
        CrUtil.showToast("地图上点击开始绘制！")
        sketchEditor?.let {
            it.stop()
            it.removeGeometryChangedListener(sketchGeometryChangeListener)
            // todo: 2023/6/6 设置编辑模式
            var model = SketchEditConfiguration()
            model.isAllowPartSelection = true
            model.isContextMenuEnabled = false
            model.isRequireSelectionBeforeDrag = true
            it.start(SketchCreationMode.POLYGON, model)
            it.opacity = 1.0f
        }
    }

    /**
     * 保存编辑的案件图斑
     */
    private fun caseSave() {
        var geo = sketchEditor?.geometry
        if (geo != null && geo.geometryType == GeometryType.POLYGON) {
            var polygon = geo as Polygon
            if (CrUnitManager.querySizeByIterable(polygon.parts[0].points) < 4) {
                showWarning("提交保存的要素少于4个点，无法提交！")
            } else {
                // todo: 2023/6/12 查询关联的案件点
                var queryParams = QueryParameters()
                queryParams.geometry = polygon
                queryParams.spatialRelationship = QueryParameters.SpatialRelationship.CONTAINS
                queryParams.whereClause = String.format(
                    "%s='%s' or %s='%s'",
                    FIELD_CASE_ISUP,
                    MEDIA_TYPE_WJ_NO,
                    FIELD_CASE_ISUP,
                    MEDIA_TYPE_WJ_YES
                )
                var queryAsync = fTableMedia?.queryFeaturesAsync(queryParams)
                queryAsync?.addDoneListener(Runnable {
                    if (queryAsync.isDone) {
                        // todo: 2023/6/12 获取查询结果
                        var featureResult = queryAsync.get().iterator()
                        var features = ArrayList<Feature>()
                        while (featureResult.hasNext()) {
                            features.add(featureResult.next())
                        }
                        // todo: 2023/6/12 如果存在违建点 则赋值 否则为 空
                        var caseId = ""
                        if (features.size > 0) {
                            caseId = features[0].attributes[FIELD_CASE_NAME].toString()
                        }
                        // todo: 2023/6/12 计算面积 并添加要素到图层
                        var area = abs(GeometryEngine.area(polygon)) * 0.0015
                        var wjFeature = fTableCasePolygon?.createFeature()
                        wjFeature!!.let {
                            it.attributes[FIELD_CASE_POLYGON_ANJID] = caseId
                            it.attributes[FIELD_CASE_POLYGON_MJ] = area
                            if (caseId == "") {
                                it.attributes[FIELD_CASE_POLYGON_BZ] =
                                    FIELD_CASE_POLYGON_BZ_VALUE_NO
                            } else {
                                it.attributes[FIELD_CASE_POLYGON_BZ] =
                                    FIELD_CASE_POLYGON_BZ_VALUE_YES
                            }
                            it.geometry = polygon
                            var appendAsync = fTableCasePolygon?.addFeatureAsync(it)
                            appendAsync?.addDoneListener(Runnable {
                                try {
                                    if (appendAsync.isDone) {
                                        showInformation("案件保存成功！")
                                        sketchEditor?.stop()
                                    }
                                } catch (ex: java.lang.IllegalArgumentException) {
                                    showError("案件保存失败！")
                                }
                            })
                        }
                    }
                })
            }
        } else {
            showWarning("提交保存的要素不符合要求！")
        }
    }

    /**
     * 删除保存的案件图斑
     */
    private fun caseDelete() {
        var queryParameters = QueryParameters()
        queryParameters.whereClause = "1=1"
        var queryAsync = fTableCasePolygon?.queryFeaturesAsync(queryParameters)
        queryAsync?.addDoneListener(Runnable {
            try {
                if (queryAsync.isDone) {
                    var deleteAsync = fTableCasePolygon?.deleteFeaturesAsync(queryAsync.get())
                    deleteAsync?.addDoneListener(Runnable {
                        try {
                            if (deleteAsync.isDone) {
                                showInformation("删除成功！")
                            }
                        } catch (ex: java.lang.IllegalArgumentException) {
                            showError("删除失败！")
                        }
                    })
                }
            } catch (ex: java.lang.IllegalArgumentException) {
                showError("删除失败！")
            }
        })
    }

    /**
     * 添加案件点到地图
     * @param location Point 点位置
     * @param longitude String 经度
     * @param latitude String 纬度
     */
    private fun caseAppendWaypointToMap(location: Point, longitude: String, latitude: String) {
        // todo: 2023/6/13 创建案件点模型
        var caseModel =
            CaseModel(longitude.toDouble(), latitude.toDouble(), 0.0, 0.0, CaseType.MediaTypeWjNo)
        // todo: 2023/6/13 创建案件点图元
        var caseFeature = fTableMedia?.createFeature()
        // todo: 2023/6/13 添加属性
        caseModel.setAttributes(caseFeature!!.attributes)
        // todo: 2023/6/13 设置要素空间图元
        caseFeature.geometry = location
        // todo: 2023/6/13 添加到地图中
        var appendAsync = fTableMedia?.addFeatureAsync(caseFeature)
        appendAsync?.addDoneListener(Runnable {
            try {
                if (appendAsync.isDone) {
                    fLayerMedia?.clearSelection()
                    fLayerMedia?.selectFeature(caseFeature)
                    caseUpdatePolygonByFeature(caseFeature, object : ICompletion<String> {
                        override fun onCompletion(completion: CompletionModel<String>) {
                            if (completion.isSuccess == true) {
                                CrUtil.showToast(
                                    String.format(
                                        "案件点追加成功 %s",
                                        caseFeature.attributes[FIELD_CASE_NAME]
                                    )
                                )
                            } else {
                                showError(completion.result!!)
                            }
                        }
                    })
                }
            } catch (ex: java.lang.IllegalArgumentException) {
                showError("案件点添加错误！")
            }
        })
    }

    /**
     * 根据案件点要素图元的案件编号查询到与之关联的案件面，并进行初始化
     * @param geom Feature 案件点要素
     * @param callback iCompletion 完成回调
     */
    private fun caseClearPolygonByFeature(geom: Feature, callback: ICompletion<String>) {
        // todo: 2023/6/13 获取案件ID
        val caseId = geom.attributes[FIELD_CASE_NAME]
        // todo: 2023/6/15 先判断是否存在与该案件点名称相同的案件图斑 将其标志清除
        var queryParameters = QueryParameters()
        queryParameters.whereClause = String.format("%s = '%s'", FIELD_CASE_POLYGON_ANJID, caseId)
        var asyncQuery = fTableCasePolygon?.queryFeaturesAsync(queryParameters)
        asyncQuery?.addDoneListener(Runnable {
            try {
                if (asyncQuery.isDone) {
                    var features = mutableListOf<Feature>()
                    for (fea in asyncQuery.get()) {
                        fea.attributes[FIELD_CASE_POLYGON_ANJID] = ""
                        fea.attributes[FIELD_CASE_POLYGON_BZ] = FIELD_CASE_POLYGON_BZ_VALUE_NO
                        features.add(fea)
                    }
                    var asyncUpdate = fTableCasePolygon?.updateFeaturesAsync(features)
                    asyncUpdate?.addDoneListener(Runnable {
                        try {
                            if (asyncUpdate.isDone) {
                                if (callback != null) callback.onCompletion(
                                    CompletionModel(
                                        true,
                                        ""
                                    )
                                )
                            }
                        } catch (ex: java.lang.IllegalArgumentException) {
                            if (callback != null) callback.onCompletion(
                                CompletionModel(
                                    false,
                                    "案件面初始化错误！"
                                )
                            )
                        }
                    })
                }
            } catch (ex: java.lang.IllegalArgumentException) {
                if (callback != null) callback.onCompletion(CompletionModel(false, "案件面初始化错误！"))
            }
        })
    }

    /**
     * 更新案件点对应的案件对象
     * @param geom Feature 案件点
     * @param callback iCompletion 回调
     */
    private fun caseUpdatePolygonByFeature(geom: Feature, callback: ICompletion<String>) {
        // todo: 2023/6/15 先进行初始化
        caseClearPolygonByFeature(geom, object : ICompletion<String> {
            override fun onCompletion(completion: CompletionModel<String>) {
                if (completion.isSuccess == false) {
                    if (callback != null) callback.onCompletion(completion)
                } else {
                    // todo: 2023/6/13 获取案件ID
                    val caseId = geom.attributes[FIELD_CASE_NAME]
                    // todo: 2023/6/15 先判断是否存在与该案件点名称相同的案件图斑 将其标志清除
                    var queryParameters = QueryParameters()
                    // todo: 2023/6/13 先进行空间查询 查询案件点对应的案件面
                    queryParameters.geometry = geom.geometry
                    queryParameters.spatialRelationship =
                        QueryParameters.SpatialRelationship.INTERSECTS
                    queryParameters.whereClause = "1=1"
                    var queryAsync = fTableCasePolygon!!.queryFeaturesAsync(queryParameters)
                    queryAsync?.addDoneListener(Runnable {
                        try {
                            if (queryAsync.isDone) {
                                var features = mutableListOf<Feature>()
                                for (fea in queryAsync.get()) {
                                    fea.attributes[FIELD_CASE_POLYGON_ANJID] = caseId
                                    fea.attributes[FIELD_CASE_POLYGON_BZ] =
                                        FIELD_CASE_POLYGON_BZ_VALUE_YES
                                    features.add(fea)
                                }
                                // todo: 2023/6/13 更新
                                var updateAsync = fTableCasePolygon!!.updateFeaturesAsync(features)
                                updateAsync?.addDoneListener(Runnable {
                                    try {
                                        if (updateAsync.isDone) {
                                            // todo: 2023/6/13 更新完成
                                            if (callback != null) callback.onCompletion(
                                                CompletionModel(
                                                    true,
                                                    "成功！"
                                                )
                                            )
                                        }
                                    } catch (ex: java.lang.IllegalArgumentException) {
                                        if (callback != null) callback.onCompletion(
                                            CompletionModel(
                                                false,
                                                "案件对象更新失败！"
                                            )
                                        )
                                    }
                                })
                            }
                        } catch (ex: java.lang.IllegalArgumentException) {
                            if (callback != null) callback.onCompletion(
                                CompletionModel(
                                    false,
                                    "案件对象查询失败！"
                                )
                            )
                        }
                    })
                }
            }
        })
    }

    /**
     * 删除案件点
     * @param screenPoint android.graphics.Point 查询产靠位置
     */
    private fun caseRemoveCaseWaypoing(screenPoint: android.graphics.Point) {
        // todo: 2023/6/14 开始查询
        var identifyAsync = map_mapView?.identifyLayerAsync(fLayerMedia, screenPoint, 6.0, false)
        identifyAsync?.addDoneListener(Runnable {
            try {
                if (identifyAsync.isDone) {
                    var features = mutableListOf<Feature>()
                    for (element in identifyAsync.get().elements) {
                        features.add(element as Feature)
                    }
                    if (features.size > 0) {
                        var dig = DialogNormal(CrApplication.getContext())
                        dig.setTitle("提示")
                        dig.setMessage("删除后将无法恢复，确定删除吗？")
                        dig.setButtonsText("删除", "取消")
                        dig.setListener(object : DialogNormal.DialogNormalListener {
                            // todo: 2023/6/14 确认删除
                            override fun completion() {
                                caseRemoveWaypointAndUpdateCasePolygon(features,
                                    object : ICompletion<String> {
                                        override fun onCompletion(completion: CompletionModel<String>) {
                                            if (completion.isSuccess == true) {
                                                CrUtil.showToast("删除成功！")
                                            } else {
                                                showError(completion.result!!)
                                            }
                                        }
                                    })
                            }

                            // todo: 2023/6/14 关闭
                            override fun close() {
                            }

                        })
                        dig.show()
                    } else {
                        showWarning("未查询到任何案件点！")
                    }
                }
            } catch (ex: java.lang.IllegalArgumentException) {
                showError("查询错误，无法删除！")
            }
        })
    }

    /**
     * 删除案件点并更新与之相对应的案件面
     * @param features List<Feature> 案件点集合
     * @param callback iCompletion 完成回调
     */
    private fun caseRemoveWaypointAndUpdateCasePolygon(
        features: List<Feature>,
        callback: ICompletion<String>
    ) {
        var deleteAsync = fTableMedia?.deleteFeaturesAsync(features)
        deleteAsync?.addDoneListener(Runnable {
            try {
                if (deleteAsync.isDone) {
                    // todo: 2023/6/16 更新
                    var wheres = mutableListOf<String>()
                    for (fea in features) {
                        wheres.add(fea.attributes[FIELD_CASE_NAME].toString())
                    }
                    caseUpdateCasePolygonByWhere(wheres, callback)
                }
            } catch (ex: java.lang.IllegalArgumentException) {
                if (callback != null) callback.onCompletion(CompletionModel(false, "案件点删除失败！"))
            }
        })
    }

    /**
     * 根据更新条件更新案件点关联的案件面为 未关联案件点状态
     * @param wheres List<String> 案件Id的集合
     * @param callback iCompletion 完成回调
     */
    private fun caseUpdateCasePolygonByWhere(wheres: List<String>, callback: ICompletion<String>) {
        var updateWhere = ""
        // todo: 2023/6/16 组合条件
        for (where in wheres) {
            updateWhere += if (updateWhere == "") {
                String.format("'%s'", where)
            } else {
                String.format(",'%s'", where)
            }
        }
        // todo: 2023/6/16 开始查询
        var queryParameters = QueryParameters()
        queryParameters.whereClause = updateWhere
        var asyncQuery = fTableCasePolygon?.queryFeaturesAsync(queryParameters)
        asyncQuery?.addDoneListener(Runnable {
            try {
                if (asyncQuery.isDone) {
                    var features = mutableListOf<Feature>()
                    for (fea in asyncQuery.get()) {
                        fea.attributes[FIELD_CASE_POLYGON_ANJID] = ""
                        fea.attributes[FIELD_CASE_POLYGON_BZ] = FIELD_CASE_POLYGON_BZ_VALUE_NO
                        features.add(fea)
                    }
                    // todo: 2023/6/16 开始更新
                    var asyncUpdate = fTableCasePolygon?.updateFeaturesAsync(features)
                    asyncUpdate?.addDoneListener(Runnable {
                        try {
                            if (asyncUpdate.isDone) {
                                if (callback != null) callback.onCompletion(
                                    CompletionModel(
                                        true,
                                        ""
                                    )
                                )
                            }
                        } catch (ex: java.lang.IllegalArgumentException) {
                            if (callback != null) callback.onCompletion(
                                CompletionModel(
                                    false,
                                    "关联案件更新失败！"
                                )
                            )
                        }
                    })
                }
            } catch (ex: java.lang.IllegalArgumentException) {
                if (callback != null) callback.onCompletion(CompletionModel(false, "关联案件查询失败！"))
            }
        })
    }

    /**
     * 移动案件点
     */
    private fun caseMoveWaypoint() {
        CrUtil.showToast("选择需要移动的案件点！")
        mapTouch?.setAction(MapAction.MapTapStartMoveWaypoing)
    }

    /**
     * 选择移动的案件点
     * @param location Point 点击位置
     */
    private fun caseMoveWaypointBySelect(location: Point) {
        // todo: 2023/6/14 设置查询区域
        var queryGeometry = GeometryEngine.buffer(location, 10.0)
        // todo: 2023/6/14 设置查询条件
        var queryParameters = QueryParameters()
        queryParameters.geometry = queryGeometry
        queryParameters.spatialRelationship = QueryParameters.SpatialRelationship.CONTAINS
        queryParameters.whereClause = "1=1"
        // todo: 2023/6/14 开始查询
        var queryAsync = fTableMedia?.queryFeaturesAsync(queryParameters)
        queryAsync?.addDoneListener(Runnable {
            try {
                if (queryAsync.isDone) {
                    var features = mutableListOf<Feature>()
                    for (fea in queryAsync.get()) {
                        features.add(fea)
                    }
                    if (features.size == 0) {
                        CrUtil.showToast("未查询到任何违建点！")
                    } else {
                        fLayerMedia?.clearSelection()
                        fLayerMedia?.selectFeature(features[0])
                        CrUtil.showToast("地图点击确定移动位置！")
                        mapTouch?.setAction(MapAction.MapTapStopMoveWaypoint)
                        // todo: 2023/6/15 利用编辑工具
                        sketchEditor?.start(location)
                        sketchEditor?.addGeometryChangedListener(sketchGeometryChangeListener)
                    }
                }
            } catch (ex: java.lang.IllegalArgumentException) {
                showError("案件点查询错误！")
            }
        })
    }

    /**
     * 案件点移动结束
     * @param location Point 移动后位置
     */
    private fun caseMoveWaypointToEnd(location: Point) {
        var asyncSelected = fLayerMedia?.selectedFeaturesAsync
        asyncSelected?.addDoneListener(Runnable {
            try {
                if (asyncSelected.isDone) {
                    var features = mutableListOf<Feature>()
                    for (fea in asyncSelected.get()) {
                        features.add(fea)
                    }
                    if (features.size == 0) {
                        showError("未获取到可移动违建点，无法完成移动操作！")
                    } else {
                        features[0].geometry = location
                        var asyncUpdate = fTableMedia?.updateFeatureAsync(features[0])
                        asyncUpdate?.addDoneListener(Runnable {
                            try {
                                if (asyncUpdate.isDone) {
                                    caseUpdatePolygonByFeature(
                                        features[0],
                                        object : ICompletion<String> {
                                            override fun onCompletion(completion: CompletionModel<String>) {
                                                if (completion.isSuccess == true) {
                                                    CrUtil.showToast("移动完成！")
                                                    sketchEditor?.removeGeometryChangedListener(
                                                        sketchGeometryChangeListener
                                                    )
                                                    sketchEditor?.stop()
                                                    fLayerMedia?.clearSelection()
                                                } else {
                                                    showError(completion.result!!)
                                                }
                                            }
                                        })
                                }
                            } catch (ex: java.lang.IllegalArgumentException) {
                                showError("移动错误，无法完成移动操作！")
                            }
                        })
                    }
                }
            } catch (ex: java.lang.IllegalArgumentException) {
                showError("获取可移动违建点错误，无法完成移动操作！")
            }
        })
    }

    /**
     * 案件分享及上传选择执行
     * @param screenPoint Point 屏幕点
     */
    private fun caseUpload(screenPoint: android.graphics.Point) {
        // todo: 2023/6/15 查询
        var asyncQuery = map_mapView?.identifyLayerAsync(fLayerMedia, screenPoint, 6.0, false)
        asyncQuery?.addDoneListener(Runnable {
            try {
                if (asyncQuery.isDone) {
                    var elements = asyncQuery.get().elements
                    if (elements.size <= 0) {
                        CrUtil.showToast("未选择上传案件！")
                    } else {
                        val caseFeature = elements.last() as Feature
                        // todo: 2023/9/1 选择要素
                        fLayerMedia?.selectFeature(caseFeature)
                        var caseModel = CaseModel(elements.last().attributes)
                        // todo: 2023/9/1 查询关联的本地案件
                        queryJoinLocalCasePolygon(caseFeature,object:ICompletion<CaseLocalPolygonModel>{
                            // todo: 2023/9/1 完成回调
                            override fun onCompletion(completion: CompletionModel<CaseLocalPolygonModel>) {
                                if(completion.isSuccess == false){
                                    showError("未关联本地案件图斑，禁止上传！")
                                }else{
                                    // todo: 2023/9/1 关联本地案件
                                    caseModel.joinPolygon = completion.result
                                    // todo: 2023/9/1 查询是否关联了网络案件图斑
                                    queryJoinNetCasePolygon(caseFeature,object:ICompletion<MutableList<String>>{
                                        // todo: 2023/9/1 查询完成
                                        override fun onCompletion(completion: CompletionModel<MutableList<String>>) {
                                            if(completion.isSuccess == true){
                                                caseModel.joinNetCaseAJHArray?.clear()
                                                for (caseId in completion.result!!){
                                                    caseModel.joinNetCaseAJHArray?.add(caseId)
                                                }
                                            }
                                            if (eventListener != null) eventListener?.onCaseUpdate(caseModel)
                                        }
                                    })
                                }
                            }
                        })
                    }
                }
            } catch (ex: java.lang.IllegalArgumentException) {
                showError("案件查询错误！")
            }
        })
    }

    /**
     * 保存截图
     * @param joinCaseName String 关联的案件名称
     */
    private fun captures(joinCaseName: String) {
        // todo: 2023/8/14 显示等待框
        showLoading("初始化...")
        // todo: 2023/8/14 初始化图片信息
        captureNames.clear()
        var tempNames = CrFileManager.getCaptureNames(joinCaseName)
        for(name in tempNames){
            captureNames.add(name)
        }
        // todo: 2023/8/14 初始化信息
        var opNameList = mutableListOf<MutableList<String>>()
        var ovLayerList = mutableListOf<MutableList<GraphicsOverlay>>()
        // todo: 2023/8/14  截图-影像图
        val opNamesYX = mutableListOf<String>(
            LAYER_NAME_YX,
            LAYER_NAME_CONFIG_VILLAGE,
            LAYER_NAME_CONFIG_TOWNS,
            LAYER_NAME_CASE
        )
        val ovLayersYX = mutableListOf<GraphicsOverlay>(gLayerIco!!)
        opNameList.add(opNamesYX)
        ovLayerList.add(ovLayersYX)
        // todo: 2023/8/14 截图-现状图
        var opNamesXZ = mutableListOf<String>(
            LAYER_NAME_XZ, LAYER_NAME_CONFIG_VILLAGE, LAYER_NAME_CONFIG_TOWNS, LAYER_NAME_CASE
        )
        var ovLayersXZ = mutableListOf<GraphicsOverlay>(gLayerIco!!)
        opNameList.add(opNamesXZ)
        ovLayerList.add(ovLayersXZ)
        // todo: 2023/8/14 截图-规划图
        var opNamesGH = mutableListOf<String>(
            LAYER_NAME_GH, LAYER_NAME_CONFIG_VILLAGE, LAYER_NAME_CONFIG_TOWNS, LAYER_NAME_CASE
        )
        var ovLayersGH = mutableListOf<GraphicsOverlay>(gLayerIco!!)
        opNameList.add(opNamesGH)
        ovLayerList.add(ovLayersGH)
        // todo: 2023/8/14 执行协程
        GlobalScope.launch {
            withContext(Dispatchers.IO) {
                updateLoading("保存地图状态...")
                saveMapLayerState()
                updateLoading("截取影像图...")
                capturesByName(opNameList[0], ovLayerList[0], captureNames[0])
                updateLoading("截取现状图...")
                capturesByName(opNameList[1], ovLayerList[1], captureNames[1])
                updateLoading("截取规划图...")
                capturesByName(opNameList[2], ovLayerList[2], captureNames[2])
                updateLoading("恢复地图状态...")
                restoreMapLayerState()
                // todo: 2023/8/14 发送截图完成事件
                mainHandler.post {
                    CrApplication.getEventBus().post(
                        EventMapCapture(
                            EventMapCapture.CaptureAction.CAPTURE_ACTION_COMPLETE,
                            null,
                            captureNames
                        )
                    )
                }
                // todo: 2023/8/14 关闭等待框
                closeLoading()
            }
        }
    }

    /**
     * 协程函数 可挂起
     * 保存地图图层当前可视状态
     */
    private suspend fun saveMapLayerState() {
        // todo: 2023/8/8 清除数据
        mapStateList.clear()
        // todo: 2023/8/8 循环操作类型图层
        for (i in 0 until mMap?.operationalLayers!!.size) {
            var visible = mMap?.operationalLayers!![i].isVisible
            mapStateList.add(MapStateModel(i, visible, MapType.MAP_TYPE_O_LAYER))
        }
        // todo: 2023/8/8 循环绘制类型图层
        for (i in 0 until map_mapView.graphicsOverlays.size) {
            var visible = map_mapView.graphicsOverlays[i].isVisible
            mapStateList.add(MapStateModel(i, visible, MapType.MAP_TYPE_D_LAYER))
        }
        delay(1000)
    }

    /**
     * 协程函数 可挂起
     * 恢复地图图层状态
     */
    private suspend fun restoreMapLayerState() {
        for (model in mapStateList) {
            if (model.mapType == MapType.MAP_TYPE_O_LAYER) {
                mMap?.operationalLayers!![model.index].isVisible = model.visible
            } else if (model.mapType == MapType.MAP_TYPE_D_LAYER) {
                map_mapView.graphicsOverlays[model.index].isVisible = model.visible
            }
        }
        delay(1000)
    }

    /**
     * 地图截图
     * @param opLayerNames List<String> 显示的操作图层名称
     * @param ovLayerNames List<GraphicsOverlay> 显示的绘制图层名称
     * @param saveName String 保存截图名称
     */
    private suspend fun capturesByName(
        opLayerNames: List<String>,
        ovLayerNames: List<GraphicsOverlay>,
        saveName: String
    ) {
        // todo: 2023/8/14 控制操作图层
        for (layer in mMap?.operationalLayers!!) {
            layer.isVisible = opLayerNames.indexOf(layer.name) != -1
        }
        // todo: 2023/8/14 操作绘制图层
        for (overlay in map_mapView.graphicsOverlays) {
            overlay.isVisible = ovLayerNames.indexOf(overlay) != -1
        }
        // todo: 2023/8/14 延迟1.5秒 为了图层能正确加载
        delay(1500)
        toBitmapAndSave("${CrUtil.IMAGE_PATH}${saveName}")
    }

    /**
     * 保存地图截图到指定路径文件
     * @param filePath String 文件全路径
     */
    private suspend fun toBitmapAndSave(filePath: String) {
        map_mapView.clearFocus()
        map_mapView.isPressed = false
        // todo: 2023/8/14 能绘制缓存就返回false
        var willNotCache = map_mapView.willNotCacheDrawing()
        map_mapView.setWillNotCacheDrawing(false)
        var color: Int = map_mapView.drawingCacheBackgroundColor
        map_mapView.drawingCacheBackgroundColor = 0
        if (color != 0) {
            map_mapView.destroyDrawingCache()
        }
        map_mapView.buildDrawingCache()
        var cacheBitmap: Bitmap? = null
        while (cacheBitmap == null) {
            var export = map_mapView.exportImageAsync()
            try {
                cacheBitmap = export.get()
            } catch (e: InterruptedException) {
                e.message?.let { CrUtil.showToast(it) }
            } catch (e: ExecutionException) {
                e.message?.let { CrUtil.showToast(it) }
            }
        }
        // todo: 2023/8/14 创建位图
        var bitmap = Bitmap.createBitmap(cacheBitmap)
        // todo: 2023/8/14 重置地图
        map_mapView.destroyDrawingCache()
        map_mapView.setWillNotCacheDrawing(willNotCache)
        map_mapView.drawingCacheBackgroundColor = color
        // todo: 2023/8/14 保存截图
        var saveFile = File(filePath)
        var stream = FileOutputStream(saveFile)
        bitmap.compress(Bitmap.CompressFormat.JPEG, 100, stream)
    }

    /**
     * 飞行器位置超边界剧中
     * @param longitude Double 经度
     * @param latitude Double 纬度
     */
    private fun aircraftCenter(longitude: Double, latitude: Double) {
        var visiblePolygon = mapView?.visibleArea
        var airPoint = Point(longitude, latitude, spatialReferenceWGS84)
        var targetPoint = GeometryEngine.project(airPoint, mapView?.spatialReference) as Point
        if (!GeometryEngine.within(targetPoint, visiblePolygon)) {
            setMapCenter(longitude, latitude, 0.0)
        }
    }

    /**
     * 初始化订阅
     */
    private fun initObserver() {
        flightControlVm.flightControlInfo.observe(requireActivity()) {
            it?.let {
                if (it.longitude > 10 && it.latitude > 10) {
                    // todo: 2023/8/17 更新位置
                    updateAirplaneLocation(it)
                    // todo: 2023/8/17 更新返航点
                    updateAirplaneHomeLocation(it)
                    // todo: 2023/8/17 更新连接线
                    updateAirplaneLinkLine(it)
                }
            }
        }
    }

    /**
     * 更新飞行器位置
     * @param obj CrFlightControlInfo 飞行器信息
     */
    private fun updateAirplaneLocation(obj: CrFlightControlInfo) {
        // todo: 2023/8/17 更新飞行器位置
        builderAirplaneLocation?.setXY(obj.longitude, obj.latitude)
        graAirplaneLocation?.geometry = builderAirplaneLocation!!.toGeometry()
        // todo: 2023/8/17 更新角度
        symbolAirplaneLocation?.angle = obj.yaw.toFloat()
        // todo: 2023/8/17 超边界更新
        aircraftCenter(obj.longitude, obj.latitude)
        // todo: 2023/8/17 更新航线
        builderAirplaneLine?.addPoint(Point(obj.longitude, obj.latitude))
        graAirplaneLine?.geometry = builderAirplaneLine!!.toGeometry()
    }

    /**
     * 更新返航位置
     * @param obj CrFlightControlInfo 飞行器信息
     */
    private fun updateAirplaneHomeLocation(obj: CrFlightControlInfo) {
        if (obj.isUpdateHomeLocation) {
            CrAudioUtil.getInstance().play(CrUnitManager.getStringFromResource(R.string.audio_go_home_reset))
            builderAirplaneHomeLocation?.setXY(obj.homeLongitude, obj.homeLatitude)
            graAirplaneHomeLocation?.geometry = builderAirplaneHomeLocation!!.toGeometry()
            // todo: 2023/8/17 如果返航点已刷新 则认为是更换了起飞位置 则重新初始化航线
            builderAirplaneLine = PolylineBuilder(spatialReferenceWGS84)
            builderAirplaneLine?.addPoint(Point(obj.homeLongitude, obj.homeLatitude))
            graAirplaneLine?.geometry = builderAirplaneLine!!.toGeometry()
            // todo: 2023/8/17 设置更新标志 否则一直更新
            obj.isUpdateHomeLocation = false
        }
    }

    /**
     * 更新连接线
     * @param obj CrFlightControlInfo 飞行器信息
     */
    private fun updateAirplaneLinkLine(obj: CrFlightControlInfo) {
        if (obj.isHomeLocationValid && obj.isLocationValid) {
            builderAirplaneLink = PolylineBuilder(spatialReferenceWGS84)
            builderAirplaneLink?.addPoint(Point(obj.longitude, obj.latitude))
            builderAirplaneLink?.addPoint(Point(obj.homeLongitude, obj.homeLatitude))
            graAirplaneLink?.geometry = builderAirplaneLink!!.toGeometry()
        }
    }

    /**
     * 查询案件点关联的本地案件
     * @param feature Feature 案件点要素
     * @param callback ICompletion<Feature>? 回调
     */
    private fun queryJoinLocalCasePolygon(feature:Feature,callback:ICompletion<CaseLocalPolygonModel>?){
        // todo: 2023/9/1 构建参数
        var param = QueryParameters()
        param.geometry = feature.geometry
        param.spatialRelationship = QueryParameters.SpatialRelationship.INTERSECTS
        param.whereClause = "1=1"
        // todo: 2023/9/1 开始查询
        var asyncQuery = fTableCasePolygon?.queryFeaturesAsync(param)
        asyncQuery?.addDoneListener {
            if(asyncQuery.isDone){
                var result = asyncQuery.get().iterator()
                var feature:Feature? = null
                while (result.hasNext()){
                    feature = result.next()
                }
                if(feature == null){
                    callback?.onCompletion(CompletionModel(false,null))
                }else{
                    // todo: 2023/9/1 创建案件图斑
                    var casePolygon = CaseLocalPolygonModel()
                    casePolygon.area = feature.attributes[FIELD_CASE_POLYGON_MJ].toString()
                    casePolygon.coords = getCoordinatesFromPolygon(feature.geometry as Polygon)
                    callback?.onCompletion(CompletionModel(true,casePolygon))
                }
            }
        }
    }

    /**
     * 获取面对象的坐标串
     * @param polygon Polygon 面
     * @return String 坐标串
     */
    private fun getCoordinatesFromPolygon(polygon:Polygon):String{
        var coordinatesList = mutableListOf<String>()
        // todo: 2023/9/1 获取点集合
        val points = polygon.parts[0].points
        for(pt in points){
            coordinatesList.add(String.format("%.6f",pt.x))
            coordinatesList.add(String.format("%.6f",pt.y))
        }
        coordinatesList.add(String.format("%.6f",points.first().x))
        coordinatesList.add(String.format("%.6f",points.first().y))
        return coordinatesList.joinToString(",")
    }


    /**
     * 查询案件点关联的网络案件
     * @param feature Feature 案件点
     * @param callback ICompletion<List<String>>? 回调
     */
    private fun queryJoinNetCasePolygon(feature:Feature, callback: ICompletion<MutableList<String>>){
        // todo: 2023/9/1 构建参数
        var param = QueryParameters()
        param.geometry = feature.geometry
        param.spatialRelationship = QueryParameters.SpatialRelationship.INTERSECTS
        param.whereClause = "1=1"
        // todo: 2023/9/1 开始查询
        var asyncQuery = fTableNetCasePolygon?.queryFeaturesAsync(param)
        asyncQuery?.addDoneListener {
            if(asyncQuery.isDone){
                var result = asyncQuery.get().iterator()
                var caseIdList = mutableListOf<String>()
                while (result.hasNext()){
                    caseIdList.add(result.next().attributes[FIELD_NET_CASE_POLYGON_AJH].toString())
                }
                if(caseIdList.size == 0){
                    callback?.onCompletion(CompletionModel(false,null))
                }else{
                    callback?.onCompletion(CompletionModel(true,caseIdList))
                }
            }
        }
    }

    /**
     * 案件上传成功后，更新案件点并删除案件面数据
     * @param caseModel CaseModel 更新成功的案件模型
     */
    private fun updateCaseAndDelPolygonBecauseUploadSuccess(caseModel:CaseModel){
        // todo: 2023/9/27 创建查询条件
        var queryParams = QueryParameters()
        queryParams.whereClause = String.format("%s='%s'", FIELD_CASE_NAME,caseModel.name)
        // todo: 2023/9/27 开始查询
        val asyncQuery = fTableMedia?.queryFeaturesAsync(queryParams)
        asyncQuery?.addDoneListener{
            if(asyncQuery.isDone){
                var result = asyncQuery.get().iterator()
                if (result.hasNext()){
                    var feature = result.next()
                    feature.attributes[FIELD_CASE_ISUP] = MEDIA_TYPE_WJ_YES
                    feature.attributes[FIELD_CASE_RQ] = CrUnitManager.toSystemYMDHMSDate()
                    // todo: 2023/9/27 更新
                    updateCasePointAttributeByFeature(feature)
                }
            }
        }
    }

    /**
     * 更新案件点属性
     * @param feature Feature
     */
    private fun updateCasePointAttributeByFeature(feature:Feature){
        val updateAsync = fTableMedia?.updateFeatureAsync(feature)
        updateAsync?.addDoneListener{
            if(updateAsync.isDone){
                // todo: 2023/9/27 更新完成 删除关联面
                deleteCasePolygonFeatureByFeature(feature)
            }
        }
    }

    /**
     * 删除案件点关联的案件面（通过空间相交分析）
     * @param feature Feature 案件点要素
     */
    private fun deleteCasePolygonFeatureByFeature(feature:Feature){
        // todo: 2023/9/27 创建查询条件
        var queryParams = QueryParameters();
        queryParams.whereClause = "1=1"
        queryParams.geometry = feature.geometry
        queryParams.spatialRelationship = QueryParameters.SpatialRelationship.INTERSECTS
        // todo: 2023/9/27 查询
        val asyncQuery = fTableCasePolygon?.queryFeaturesAsync(queryParams)
        asyncQuery?.addDoneListener{
            if(asyncQuery.isDone){
                // todo: 2023/9/27 查询完成后 删除
                val result = asyncQuery.get().iterator()
                val delFeatures = mutableListOf<Feature>()
                while (result.hasNext()){
                    delFeatures.add(result.next())
                }
                val asyncDelete = fTableCasePolygon?.deleteFeaturesAsync(delFeatures)
                asyncDelete?.addDoneListener{
                    if(asyncDelete.isDone){
                        // todo: 2023/9/27 删除成功
                        showInformation("地图案件已更新！")
                    }
                }
            }
        }
    }


    /**
     * 订阅地图事件执行动作
     * @param event EventMap 事件
     */
    @Subscribe
    fun onAction(event: EventMap) {
        when (event.action) {
            // todo: 2023/4/17 绘制涂鸦
            MapAction.MapTapDrawDoodle -> {
                CrUtil.showToast("地图接收绘制命令")
                doodleStartDraw()
            }
            // todo: 2023/4/17 清除涂鸦
            MapAction.EventDoodleClear -> {
                clearSketch()
            }
            // todo: 2023/4/17 保存涂鸦
            MapAction.EventDoodleSave -> {
                doodleSave()
            }
            // todo: 2023/4/17 停止草图编辑
            MapAction.EventStopSketch -> {
                sketchEditor?.stop()
            }
            // todo: 2023/4/17 全部删除保存的涂鸦
            MapAction.EventDoodleRemove -> {
                removeAllFeatureByTable(fTableDoodle!!)
            }
            // todo: 2023/4/18 删除选择涂鸦
            MapAction.EventDoodleDelete -> {
                doodleSelectFeature(fLayerDoodle!!)
            }
            // todo: 2023/4/18 选择涂鸦
            MapAction.MapTapSelectDoodle -> {
                CrUtil.showToast("地图上点击需要选择的涂鸦")
                mapTouch?.setQueryLayer(fLayerDoodle!!, MapAction.MapTapSelectDoodle)
            }
            // todo: 2023/4/18 停止Touch
            MapAction.EventStopTouch -> {
                mapTouch?.setQueryLayer(null, null)
                unfocusAllFeature()
            }
            // todo: 2023/4/19 绘制标志
            MapAction.MapTapAppendMark -> {
                CrUtil.showToast("地图上单击创建标志！")
                if (event.owner is FragmentMark) {
                    markChange = (event.owner as FragmentMark).getMark()
                    mapTouch?.setAction(MapAction.MapTapAppendMark)
                }
            }
            // todo: 2023/4/19 清除标志
            MapAction.EventMarkClear -> {
                gLayerIco?.let {
                    it.graphics?.clear()
                    CrUtil.showToast("标志清除完成！")
                }
            }
            // todo: 2023/4/19 标志保存
            MapAction.EventMarkSave -> {
                markSave()
            }
            // todo: 2023/4/19 删除保存的全部标志
            MapAction.EventMarkRemove -> {
                removeAllFeatureByTable(fTableMark!!)
            }
            // todo: 2023/4/21 选择标志
            MapAction.MapTapSelectMark -> {
                CrUtil.showToast("地图上点击需要选择的标志")
                mapTouch?.setQueryLayer(fLayerMark!!, MapAction.MapTapSelectMark)
            }
            // todo: 2023/4/21 删除选择的标志
            MapAction.EventMarkDelete -> {
                doodleSelectFeature(fLayerMark!!)
            }
            // todo: 2023/4/21 长度测量
            MapAction.EventSurveyLength -> {
                measureLength()
            }
            // todo: 2023/6/6 面积测量
            MapAction.EventSurveyArea -> {
                measureArea()
            }
            // todo: 2023/6/7 初始化测量
            MapAction.EventSurveyClear -> {
                // todo: 2023/6/7 清理临时标注
                gLayerTemp?.graphics?.clear()
                // todo: 2023/6/7 结束编辑
                sketchEditor?.let {
                    it.stop()
                    it.removeGeometryChangedListener(sketchGeometryChangeListener)
                }
            }
            // todo: 2023/6/7 获取地图位置
            MapAction.MapTapGetLocation -> {
                CrUtil.showToast("地图上点击查询地理位置")
                mapTouch?.setAction(MapAction.MapTapGetLocation)
            }
            // todo: 2023/6/7 输入坐标定位
            MapAction.EventInputLocationToMap -> {
                var dialogInput: DialogInput = DialogInput(requireContext(), "输入位置信息")
                dialogInput.setButtonsText("确定", "关闭")
                dialogInput.setHints("输入经度，例118.70687", "输入纬度，例35.218991")
                dialogInput.setFonts(
                    getString(R.string.ico_location),
                    getString(R.string.ico_location)
                )
                dialogInput.setListener(inputLocationToMapListener)
                dialogInput.show()
            }
            // todo: 2023/6/9 开始绘制案件图斑
            MapAction.EventCaseDrawPolygon -> {
                caseStartDraw()
            }
            // todo: 2023/6/9 重置绘制
            MapAction.EventCaseDrawReset -> {
                caseStartDraw()
            }
            // todo: 2023/6/9 回退
            MapAction.EventCaseDrawUndo -> {
                sketchEditor?.undo()
            }
            // todo: 2023/6/12 保存绘制的图斑
            MapAction.EventCaseSavePolygon -> {
                caseSave()
            }
            // todo: 2023/6/12 删除保存的图斑
            MapAction.EventCaseRemovePolygon -> {
                caseDelete()
            }
            // todo: 2023/6/12 地图点击添加违建点
            MapAction.MapTapAddWaypoint -> {
                CrUtil.showToast("地图上点击添加案件点！")
                mapTouch?.setAction(MapAction.MapTapAddWaypoint)
            }
            // todo: 2023/6/13 删除案件点
            MapAction.MapTapDeleteWaypoint -> {
                CrUtil.showToast("地图上选择需要删除的违建点后删除！")
                mapTouch?.setAction(MapAction.MapTapDeleteWaypoint)
            }
            // todo: 2023/6/14 移动案件点
            MapAction.MapTapStartMoveWaypoing -> {
                caseMoveWaypoint()
            }
            // todo: 2023/6/15 案件分享及上传
            MapAction.MapTapCaseWxAndUpload -> {
                CrUtil.showToast("地图上选择需要上传或分享的案件！")
                mapTouch?.setAction(MapAction.MapTapCaseWxAndUpload)
            }
            // todo: 2023/9/27 案件上传成功
            MapAction.EventCaseUploadSuccess->{
               var caseModel =  event.owner as CaseModel
                // todo: 2023/9/27 更新案件点为已上传 删除面对象
                updateCaseAndDelPolygonBecauseUploadSuccess(caseModel)
            }
        }
    }

    /**
     * 订阅截屏事件
     */
    @Subscribe
    fun onCapture(event: EventMapCapture) {
        if (event.action == EventMapCapture.CaptureAction.CAPTURE_ACTION_START) {
            captures(event.joinCaseName!!)
        }
    }

    /**
     * 设置事件监听
     * @param listener EventListener 事件监听
     */
    fun crSetEventListener(listener: EventListener) {
        this.eventListener = listener
    }

    // todo: 2023/4/17 生命周期
    override fun onDestroy() {
        // todo: 2023/4/17 移除订阅监听
        CrApplication.getEventBus().unregister(this)
        super.onDestroy()
    }

    /**
     * 生命周期
     * 后台转前台
     */
    override fun onResume() {
        super.onResume()
        // todo: 2023/8/17 初始化订阅
        initObserver()
    }
}