From 07d316581321a32f47b4b37d488eed53d967e73c Mon Sep 17 00:00:00 2001
From: squallzhjch <zhangjingchao@navinfo.com>
Date: Fri, 7 Jul 2023 13:54:08 +0800
Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=BE=BF=E7=AD=BE=E5=8A=9F?=
 =?UTF-8?q?=E8=83=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 app/build.gradle                              |    4 +
 .../omqs/ui/activity/map/MainActivity.kt      |   62 +-
 .../omqs/ui/activity/map/MainViewModel.kt     |   29 +-
 .../omqs/ui/fragment/empty/EmptyFragment.kt   |   19 +-
 .../EvaluationResultFragment.kt               |   41 +-
 .../EvaluationResultViewModel.kt              |   53 +-
 .../omqs/ui/fragment/note/CanvasFragment.kt   |  154 ++
 .../omqs/ui/fragment/note/CanvasView.kt       | 1801 +++++++++++++++++
 .../omqs/ui/fragment/note/CanvasViewHelper.kt |  424 ++++
 .../omqs/ui/fragment/note/NoteFragment.kt     |  135 ++
 .../omqs/ui/fragment/note/NoteViewModel.kt    |  171 ++
 .../res/drawable-xxhdpi/home_map_center.png   |  Bin 0 -> 2150 bytes
 .../main/res/drawable-xxhdpi/sketch_back.png  |  Bin 0 -> 1577 bytes
 .../res/drawable-xxhdpi/sketch_eraser.png     |  Bin 0 -> 1599 bytes
 .../res/drawable-xxhdpi/sketch_forward.png    |  Bin 0 -> 1548 bytes
 .../drawable/selector_sketch_eraser_bg.xml    |   16 +
 .../drawable/shape_vertical_dashed_line.xml   |    2 +-
 app/src/main/res/layout/activity_main.xml     |   46 +-
 app/src/main/res/layout/fragment_canvas.xml   |    9 +
 app/src/main/res/layout/fragment_note.xml     |  235 +++
 .../main/res/layout/fragment_phenomenon.xml   |   44 +-
 .../main/res/layout/fragment_problem_link.xml |   10 +-
 .../navigation/middle_fragment_nav_graph.xml  |    6 +
 .../navigation/right_fragment_nav_graph.xml   |    7 +
 app/src/main/res/values/styleable.xml         |    7 +
 app/src/main/res/values/styles.xml            |   15 +
 collect-library/build.gradle                  |    2 +-
 .../collect/library/data/entity/NoteBean.kt   |   13 +
 .../data/entity/SketchAttachContent.kt        |   40 +
 .../library/map/handler/BaseHandler.kt        |    2 +
 .../library/map/handler/LineHandler.kt        |    7 +-
 .../library/map/handler/MarkHandler.kt        |  181 +-
 .../library/map/handler/ViewportHandler.kt    |   57 +-
 .../library/map/layers/NoteLineLayer.kt       |   98 +
 .../main/res/drawable/icon_note_marker.xml    |   12 +
 settings.gradle                               |   13 +-
 36 files changed, 3588 insertions(+), 127 deletions(-)
 create mode 100644 app/src/main/java/com/navinfo/omqs/ui/fragment/note/CanvasFragment.kt
 create mode 100644 app/src/main/java/com/navinfo/omqs/ui/fragment/note/CanvasView.kt
 create mode 100644 app/src/main/java/com/navinfo/omqs/ui/fragment/note/CanvasViewHelper.kt
 create mode 100644 app/src/main/java/com/navinfo/omqs/ui/fragment/note/NoteFragment.kt
 create mode 100644 app/src/main/java/com/navinfo/omqs/ui/fragment/note/NoteViewModel.kt
 create mode 100644 app/src/main/res/drawable-xxhdpi/home_map_center.png
 create mode 100644 app/src/main/res/drawable-xxhdpi/sketch_back.png
 create mode 100644 app/src/main/res/drawable-xxhdpi/sketch_eraser.png
 create mode 100644 app/src/main/res/drawable-xxhdpi/sketch_forward.png
 create mode 100644 app/src/main/res/drawable/selector_sketch_eraser_bg.xml
 create mode 100644 app/src/main/res/layout/fragment_canvas.xml
 create mode 100644 app/src/main/res/layout/fragment_note.xml
 create mode 100644 app/src/main/res/values/styleable.xml
 create mode 100644 collect-library/src/main/java/com/navinfo/collect/library/data/entity/NoteBean.kt
 create mode 100644 collect-library/src/main/java/com/navinfo/collect/library/data/entity/SketchAttachContent.kt
 create mode 100644 collect-library/src/main/java/com/navinfo/collect/library/map/layers/NoteLineLayer.kt
 create mode 100644 collect-library/src/main/res/drawable/icon_note_marker.xml

diff --git a/app/build.gradle b/app/build.gradle
index ab7fa750..03268d5c 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -127,6 +127,10 @@ dependencies {
     implementation "androidx.camera:camera-view:1.0.0-alpha24"
 
     implementation 'com.google.mlkit:barcode-scanning:16.1.1'
+
+    //图片加载
+    implementation 'com.github.bumptech.glide:glide:4.15.1'
+    annotationProcessor 'com.github.bumptech.glide:compiler:4.15.1'
 }
 //允许引用生成的代码
 kapt {
diff --git a/app/src/main/java/com/navinfo/omqs/ui/activity/map/MainActivity.kt b/app/src/main/java/com/navinfo/omqs/ui/activity/map/MainActivity.kt
index e9d14e89..2234802e 100644
--- a/app/src/main/java/com/navinfo/omqs/ui/activity/map/MainActivity.kt
+++ b/app/src/main/java/com/navinfo/omqs/ui/activity/map/MainActivity.kt
@@ -184,8 +184,39 @@ class MainActivity : BaseActivity() {
         }
         //捕捉列表变化回调
         viewModel.liveDataQsRecordIdList.observe(this) {
-            //处理页面跳转
-            viewModel.navigationRightFragment(this, it)
+            //跳转到质检数据页面
+            //获取右侧fragment容器
+            val naviController = findNavController(R.id.main_activity_right_fragment)
+
+            naviController.currentDestination?.let { navDestination ->
+                when (navDestination.id) {
+                    R.id.RightEmptyFragment -> {
+                        if (it.size == 1) {
+                            val bundle = Bundle()
+                            bundle.putString("QsId", it[0])
+                            naviController.navigate(R.id.EvaluationResultFragment, bundle)
+                        }
+                    }
+                }
+            }
+        }
+        //捕捉列表变化回调
+        viewModel.liveDataNoteIdList.observe(this) {
+            //跳转到质检数据页面
+            //获取右侧fragment容器
+            val naviController = findNavController(R.id.main_activity_right_fragment)
+
+            naviController.currentDestination?.let { navDestination ->
+                when (navDestination.id) {
+                    R.id.RightEmptyFragment -> {
+                        if (it.size == 1) {
+                            val bundle = Bundle()
+                            bundle.putString("NoteId", it[0])
+                            naviController.navigate(R.id.NoteFragment, bundle)
+                        }
+                    }
+                }
+            }
         }
         //右上角菜单是否被点击
         viewModel.liveDataMenuState.observe(this) {
@@ -253,7 +284,7 @@ class MainActivity : BaseActivity() {
             }
         }
 
-        viewModel.liveDataSignMoreInfo.observe(this){
+        viewModel.liveDataSignMoreInfo.observe(this) {
             val fragment =
                 supportFragmentManager.findFragmentById(R.id.main_activity_sign_more_info_fragment)
             if (fragment == null) {
@@ -432,10 +463,17 @@ class MainActivity : BaseActivity() {
     /**
      * 隐藏或显示右侧展开按钮
      */
-    fun setRightSwitchButton(visibility: Int) {
+    fun setRightSwitchButtonVisibility(visibility: Int) {
         binding.mainActivityFragmentSwitch.visibility = visibility
     }
 
+    /**
+     * 顶部菜单按钮
+     */
+    fun setTopMenuButtonVisibility(visibility: Int) {
+        binding.mainActivityMenu.visibility = visibility
+    }
+
     /**
      * 点击录音按钮
      */
@@ -557,4 +595,20 @@ class MainActivity : BaseActivity() {
             viewModel.showSignMoreInfo(viewModel.liveDataRoadName.value!!)
         }
     }
+
+    /**
+     * 新增便签,打开便签fragment
+     */
+    fun onClickNewNote() {
+        rightController.navigate(R.id.NoteFragment)
+        binding.mainActivityMenu.isSelected = false
+        binding.mainActivityMenuGroup.visibility = View.INVISIBLE
+    }
+
+    /**
+     * 右侧按钮+经纬度按钮
+     */
+    fun setRightButtonsVisible(visible: Int) {
+        binding.mainActivityRightVisibilityButtonsGroup2.visibility = visible
+    }
 }
\ No newline at end of file
diff --git a/app/src/main/java/com/navinfo/omqs/ui/activity/map/MainViewModel.kt b/app/src/main/java/com/navinfo/omqs/ui/activity/map/MainViewModel.kt
index b456f1b1..33720482 100644
--- a/app/src/main/java/com/navinfo/omqs/ui/activity/map/MainViewModel.kt
+++ b/app/src/main/java/com/navinfo/omqs/ui/activity/map/MainViewModel.kt
@@ -71,6 +71,9 @@ class MainViewModel @Inject constructor(
     //地图点击捕捉到的质检数据ID列表
     val liveDataQsRecordIdList = MutableLiveData<List<String>>()
 
+    //地图点击捕捉到的标签ID列表
+    val liveDataNoteIdList = MutableLiveData<List<String>>()
+
     //左侧看板数据
     val liveDataSignList = MutableLiveData<List<SignBean>>()
 
@@ -128,6 +131,10 @@ class MainViewModel @Inject constructor(
             override fun onQsRecordList(list: MutableList<String>) {
                 liveDataQsRecordIdList.value = list
             }
+
+            override fun onNoteList(list: MutableList<String>) {
+                liveDataNoteIdList.value = list
+            }
         })
         initLocation()
         //处理地图点击操作
@@ -295,7 +302,8 @@ class MainViewModel @Inject constructor(
                         }
                     }
 
-                    liveDataTopSignList.postValue(topSignList.distinctBy { it.name }.sortedBy { it.index })
+                    liveDataTopSignList.postValue(topSignList.distinctBy { it.name }
+                        .sortedBy { it.index })
 
                     liveDataSignList.postValue(signList.sortedBy { it.distance })
                     val speechText = SignUtil.getRoadSpeechText(topSignList)
@@ -470,25 +478,6 @@ class MainViewModel @Inject constructor(
         }
     }
 
-    /**
-     * 处理页面调转
-     */
-    fun navigationRightFragment(activity: MainActivity, list: List<String>) {
-        //获取右侧fragment容器
-        val naviController = activity.findNavController(R.id.main_activity_right_fragment)
-
-        naviController.currentDestination?.let { navDestination ->
-            when (navDestination.id) {
-                R.id.RightEmptyFragment -> {
-                    if (list.size == 1) {
-                        val bundle = Bundle()
-                        bundle.putString("QsId", list[0])
-                        naviController.navigate(R.id.EvaluationResultFragment, bundle)
-                    }
-                }
-            }
-        }
-    }
 
     /**
      * 开启线选择
diff --git a/app/src/main/java/com/navinfo/omqs/ui/fragment/empty/EmptyFragment.kt b/app/src/main/java/com/navinfo/omqs/ui/fragment/empty/EmptyFragment.kt
index f449b3fc..0792fa32 100644
--- a/app/src/main/java/com/navinfo/omqs/ui/fragment/empty/EmptyFragment.kt
+++ b/app/src/main/java/com/navinfo/omqs/ui/fragment/empty/EmptyFragment.kt
@@ -32,18 +32,27 @@ class EmptyFragment : Fragment() {
     override fun onStart() {
         super.onStart()
         val currentDestination = findNavController().currentDestination
-        //有右侧面板的时候
-        if (currentDestination?.label == "右侧空页面") {
-            currentDestinationLabel = "右侧空页面"
-            (activity as MainActivity).setRightSwitchButton(View.GONE)
+        currentDestination?.let {
+            //有右侧面板的时候
+            currentDestinationLabel = it.label.toString()
+            if (it.label == "右侧空页面") {
+                (activity as MainActivity).setRightSwitchButtonVisibility(View.GONE)
+                (activity as MainActivity).setTopMenuButtonVisibility(View.VISIBLE)
+            } else if (it.label == "中间空页面") {
+                (activity as MainActivity).setRightButtonsVisible(View.VISIBLE)
+            }
         }
+
     }
 
     override fun onStop() {
         super.onStop()
         //没有有右侧面板的时候
         if (currentDestinationLabel == "右侧空页面") {
-            (activity as MainActivity).setRightSwitchButton(View.VISIBLE)
+            (activity as MainActivity).setRightSwitchButtonVisibility(View.VISIBLE)
+            (activity as MainActivity).setTopMenuButtonVisibility(View.GONE)
+        } else if (currentDestinationLabel == "中间空页面") {
+            (activity as MainActivity).setRightButtonsVisible(View.GONE)
         }
     }
 
diff --git a/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/EvaluationResultFragment.kt b/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/EvaluationResultFragment.kt
index 6dd0d883..270654cd 100644
--- a/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/EvaluationResultFragment.kt
+++ b/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/EvaluationResultFragment.kt
@@ -2,11 +2,14 @@ package com.navinfo.omqs.ui.fragment.evaluationresult
 
 import android.app.Activity
 import android.content.Intent
+import android.graphics.Bitmap
 import android.os.Build
 import android.os.Bundle
 import android.provider.MediaStore
 import android.util.Log
 import android.view.*
+import androidx.activity.result.ActivityResult
+import androidx.activity.result.ActivityResultLauncher
 import androidx.activity.result.contract.ActivityResultContracts
 import androidx.databinding.DataBindingUtil
 import androidx.navigation.NavOptions
@@ -27,6 +30,7 @@ import dagger.hilt.android.AndroidEntryPoint
 @AndroidEntryPoint
 class EvaluationResultFragment : BaseFragment(), View.OnClickListener {
     private lateinit var binding: FragmentEvaluationResultBinding
+    private var mCameraLauncher: ActivityResultLauncher<Intent>? = null
 
     /**
      * 和[PhenomenonFragment],[ProblemLinkFragment],[EvaluationResultFragment]共用同一个viewModel
@@ -37,6 +41,23 @@ class EvaluationResultFragment : BaseFragment(), View.OnClickListener {
         PictureAdapter()
     }
 
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        mCameraLauncher = registerForActivityResult<Intent, ActivityResult>(
+            ActivityResultContracts.StartActivityForResult()
+        ) { result: ActivityResult ->
+            if (result.resultCode == Activity.RESULT_OK) {
+                // 处理相机返回的结果
+                val extras = result.data!!.extras
+                val imageBitmap: Bitmap? = extras!!["data"] as Bitmap?
+                // 在这里处理图片数据
+                if (imageBitmap != null)
+                    viewModel.savePhoto(imageBitmap)
+            }
+        }
+    }
+
     //    private val args:EmptyFragmentArgs by navArgs()
     override fun onCreateView(
         inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
@@ -62,7 +83,7 @@ class EvaluationResultFragment : BaseFragment(), View.OnClickListener {
             adapter.refreshData(it)
         }
 
-        binding.evaluationPictureViewpager
+
 
         return binding.root
     }
@@ -122,7 +143,7 @@ class EvaluationResultFragment : BaseFragment(), View.OnClickListener {
 
                 val recyclerView = viewPager.getChildAt(0) as RecyclerView
 
-                recyclerView.setPadding(0, 0, width / 2, 0)
+                recyclerView.setPadding(0, 0, width / 2 - 30, 0)
                 recyclerView.clipToPadding = false
             }
         })
@@ -373,18 +394,10 @@ class EvaluationResultFragment : BaseFragment(), View.OnClickListener {
 
     private fun takePhoto() {
         try {
-            val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
-
-            val someActivityResultLauncher =
-                registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
-                    if (result.resultCode == Activity.RESULT_OK) {
-                        val data: Intent? = result.data
-                        // 处理返回的结果
-                    }
-                }
-
-            someActivityResultLauncher.launch(intent)
-
+            val takePictureIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
+            if (takePictureIntent.resolveActivity(requireActivity().packageManager) != null) {
+                mCameraLauncher!!.launch(takePictureIntent)
+            }
         } catch (e: Exception) {
             Log.d("TTTT", e.toString())
         }
diff --git a/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/EvaluationResultViewModel.kt b/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/EvaluationResultViewModel.kt
index aa679ccb..a8b6ca0a 100644
--- a/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/EvaluationResultViewModel.kt
+++ b/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/EvaluationResultViewModel.kt
@@ -3,6 +3,7 @@ package com.navinfo.omqs.ui.fragment.evaluationresult
 import android.app.Activity
 import android.app.Dialog
 import android.content.Context
+import android.graphics.Bitmap
 import android.graphics.drawable.AnimationDrawable
 import android.graphics.drawable.BitmapDrawable
 import android.os.Build
@@ -35,17 +36,13 @@ import com.navinfo.omqs.util.DateTimeUtil
 import com.navinfo.omqs.util.SoundMeter
 import com.navinfo.omqs.util.SpeakMode
 import dagger.hilt.android.lifecycle.HiltViewModel
-import io.realm.OrderedCollectionChangeSet
 import io.realm.Realm
 import io.realm.RealmList
-import io.realm.RealmModel
-import io.realm.RealmResults
-import io.realm.kotlin.addChangeListener
-import io.realm.kotlin.where
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.launch
 import org.oscim.core.GeoPoint
 import java.io.File
+import java.io.FileOutputStream
 import java.util.*
 import javax.inject.Inject
 
@@ -78,9 +75,20 @@ class EvaluationResultViewModel @Inject constructor(
      */
     val liveDataRightTypeList = MutableLiveData<List<RightBean>>()
 
-    var liveDataQsRecordBean = MutableLiveData<QsRecordBean>()
+    /**
+     *
+     */
+    val liveDataQsRecordBean = MutableLiveData<QsRecordBean>()
 
-    var listDataChatMsgEntityList = MutableLiveData<MutableList<ChatMsgEntity>>()
+    /**
+     * 语音列表
+     */
+    val listDataChatMsgEntityList = MutableLiveData<MutableList<ChatMsgEntity>>()
+
+    /**
+     * 照片列表
+     */
+    val liveDataPictureList = MutableLiveData<MutableList<String>>()
 
     var oldBean: QsRecordBean? = null
 
@@ -520,9 +528,38 @@ class EvaluationResultViewModel @Inject constructor(
             mSoundMeter!!.stop()
         }
         pop?.let {
-            if(it.isShowing){
+            if (it.isShowing) {
                 it.dismiss()
             }
         }
     }
+
+
+    fun savePhoto(bitmap: Bitmap) {
+        viewModelScope.launch(Dispatchers.IO) {
+            // 创建一个名为 "MyApp" 的文件夹
+            val myAppDir = File(Constant.USER_DATA_ATTACHEMNT_PATH)
+            if (!myAppDir.exists())
+                myAppDir.mkdirs() // 确保文件夹已创建
+
+            // 创建一个名为 fileName 的文件
+            val file = File(myAppDir, "${UUID.randomUUID()}.png")
+            file.createNewFile() // 创建文件
+
+            // 将 Bitmap 压缩为 JPEG 格式,并将其写入文件中
+            val out = FileOutputStream(file)
+            bitmap.compress(Bitmap.CompressFormat.JPEG, 90, out)
+            out.flush()
+            out.close()
+            var picList = mutableListOf<String>()
+            if (liveDataPictureList.value == null) {
+                picList.add(file.absolutePath)
+            } else {
+                picList.addAll(liveDataPictureList.value!!)
+                picList.add(file.absolutePath)
+            }
+            liveDataPictureList.postValue(picList)
+        }
+
+    }
 }
\ No newline at end of file
diff --git a/app/src/main/java/com/navinfo/omqs/ui/fragment/note/CanvasFragment.kt b/app/src/main/java/com/navinfo/omqs/ui/fragment/note/CanvasFragment.kt
new file mode 100644
index 00000000..09ca56e9
--- /dev/null
+++ b/app/src/main/java/com/navinfo/omqs/ui/fragment/note/CanvasFragment.kt
@@ -0,0 +1,154 @@
+package com.navinfo.omqs.ui.fragment.note
+
+import android.os.Build
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.databinding.DataBindingUtil
+import com.navinfo.omqs.R
+import com.navinfo.omqs.databinding.FragmentCanvasBinding
+import com.navinfo.omqs.databinding.FragmentNoteBinding
+import com.navinfo.omqs.databinding.FragmentProblemLinkBinding
+import com.navinfo.omqs.ui.fragment.BaseFragment
+import com.navinfo.omqs.ui.fragment.note.CanvasView.CanvasStyle
+import com.navinfo.omqs.ui.fragment.note.CanvasView.OnCanvasChangeListener
+import com.navinfo.omqs.ui.other.shareViewModels
+
+/**
+ * @author zhjch
+ * @version V1.0
+ * @ClassName: CanvasFragment
+ * @Date 2016/5/10
+ * @Description: ${TODO}(绘制画布)
+ */
+class CanvasFragment : BaseFragment() {
+    /**
+     * 获取画布
+     *
+     * @return
+     */
+    /**
+     * 画布
+     */
+    private val canvasView by lazy { binding.canvasView }
+
+    /**
+     * 画笔线型
+     */
+    private var mStyle = CanvasStyle.FREE_LINE
+
+    /**
+     * 画笔颜色
+     */
+    private var mColor = -1
+
+    /**
+     * 画笔粗细
+     */
+    private var width = 5
+
+
+    /**
+     * 画布回调接口
+     */
+    private var listener: OnCanvasChangeListener? = null
+
+
+    private var _binding: FragmentCanvasBinding? = null
+    private val binding get() = _binding!!
+
+    private val viewModel by shareViewModels<NoteViewModel>("note")
+
+    override fun onCreateView(
+        inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
+    ): View {
+        _binding = FragmentCanvasBinding.inflate(inflater, container, false)
+        viewModel.initCanvasView(canvasView)
+        return binding.root
+    }
+
+    override fun onDestroyView() {
+        super.onDestroyView()
+        _binding = null
+    }
+
+    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+        super.onViewCreated(view, savedInstanceState)
+
+        canvasView.setStyle(mStyle)
+        if (mColor == -1) {
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+                mColor = resources.getColor(R.color.black, null)
+            }
+        }
+        canvasView.setPaintColor(mColor)
+        canvasView.setPaintWidth(width)
+        if (listener != null) {
+            canvasView.setOnCanvasChangeListener(listener)
+        }
+
+//         * 开关橡皮擦
+//         */
+//        viewModel.liveEraserData.observe(viewLifecycleOwner) {
+//            canvasView.setEraser(it)
+//        }
+//        /**
+//         * 清除
+//         */
+//        viewModel.liveClearData.observe(viewLifecycleOwner) {
+//            canvasView.removeAllPaint()
+//        }
+//        /**
+//         * 回退上一笔
+//         */
+//        viewModel.liveBackData.observe(viewLifecycleOwner) {
+//            canvasView.back()
+//        }
+//        /**
+//         * 撤销回退
+//         */
+//        viewModel.liveForward.observe(viewLifecycleOwner) {
+//            canvasView.forward()
+//        }
+//
+
+    }
+
+
+    /**
+     * 将数据转化并绘制在画板上
+     *
+     * @param value
+     */
+    fun setDrawPathList(value: MutableList<CanvasView.DrawPath>) {
+        if (value != null && value.isNotEmpty()) {
+            canvasView.setDrawPathList(value)
+        }
+    }
+
+
+    /**
+     * 设置草图画笔线型
+     */
+    fun setStyle(style: CanvasStyle) {
+        mStyle = style
+        canvasView.setStyle(style)
+    }
+
+    /**
+     * 设置画笔颜色
+     */
+    fun setPaintColor(color: Int) {
+        mColor = color
+        canvasView.setPaintColor(mColor)
+    }
+
+    /**
+     * 设置画笔粗细
+     */
+    fun setPaintWidth(width: Int) {
+        this.width = width
+        canvasView.setPaintWidth(width)
+    }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/navinfo/omqs/ui/fragment/note/CanvasView.kt b/app/src/main/java/com/navinfo/omqs/ui/fragment/note/CanvasView.kt
new file mode 100644
index 00000000..c8797d46
--- /dev/null
+++ b/app/src/main/java/com/navinfo/omqs/ui/fragment/note/CanvasView.kt
@@ -0,0 +1,1801 @@
+package com.navinfo.omqs.ui.fragment.note
+
+import android.content.Context
+import android.graphics.*
+import android.media.ThumbnailUtils
+import android.util.AttributeSet
+import android.util.Log
+import android.view.MotionEvent
+import android.view.View
+import com.navinfo.collect.library.utils.GeometryTools
+import com.navinfo.omqs.R
+import com.navinfo.omqs.ui.other.BaseToast
+import kotlin.math.abs
+import kotlin.math.cos
+import kotlin.math.sin
+
+/**
+ * @author zhjch
+ * @version V1.0
+ * @ClassName: CanvasView
+ * @Date 2016/5/10
+ * @Description: ${TODO}(画板)
+ */
+class CanvasView @JvmOverloads constructor(
+    private val mContext: Context,
+    attrs: AttributeSet? = null,
+    defStyle: Int = 0
+) : View(
+    mContext, attrs, defStyle
+) {
+    /**
+     * 画笔类型
+     */
+    enum class CanvasStyle {
+        FREE_LINE,  //自由线
+        STRAIGHT_LINE,  //直线
+        RECT_LINE,  //框
+        RAILWAY_LINE,  // 铁路线
+        GREENLAND_LINE,  //绿地线
+        WATER_LINE,  //水系线
+        BUILDING_LINE,  //建筑物
+        POLY_LINE,  //折线
+        CIRCULAR_POINT,  //圆点
+        ELLIPSE_LINE,  //椭圆
+        PARKING_LINE
+        //停车场
+    }
+
+    /**
+     * 画布
+     */
+    private var mCanvas: Canvas? = null
+
+    /**
+     * 控制线型
+     */
+    private var mPath: Path? = null
+
+    /**
+     * 画布背景
+     */
+    private var mBitmapPaint: Paint? = null
+
+    /**
+     * 画布前景
+     */
+    private var mBitmap: Bitmap? = null
+
+    /**
+     * 普通画笔
+     */
+    private val mPaint: Paint?
+
+    /**
+     * 圆形画笔
+     */
+    private val mPaintCircular: Paint
+
+    /**
+     * 铁路画笔1 底色
+     */
+    private val mPaintRailway1: Paint
+
+    /**
+     * 铁路画笔2 前景色
+     */
+    private val mPaintRailway2: Paint
+
+    /**
+     * 水系画笔1 边框
+     */
+    private val mPaintWater: Paint
+
+    /**
+     * 水系画笔2 填充
+     */
+    private val mPaintWater1: Paint
+
+    /**
+     * 绿地画笔1 边框
+     */
+    private val mPaintGreenland: Paint
+
+    /**
+     * 绿地画笔2 填充
+     */
+    private val mPaintGreenland1: Paint
+
+    /**
+     * 停车场1 边框
+     */
+    private val mPaintParking: Paint
+
+    /**
+     * 停车场 填充
+     */
+    private val mPaintParking1: Paint
+
+    /**
+     * 准星图片
+     */
+    private val mStarBitmap: Bitmap
+
+    /**
+     * 铁路栅格线样式
+     */
+    private val pathEffect1 = DashPathEffect(floatArrayOf(16f, 16f), 4f)
+    //    private DashPathEffect pathEffect2 = new DashPathEffect(new float[] { 12,12 }, 12);
+    /**
+     * 需不需要记录画笔轨迹点
+     */
+    private var isSavePoint: Boolean
+
+    /**
+     * 是否在move 中
+     */
+    private var isMove = false
+
+    /**
+     * 是否打开橡皮擦
+     */
+    private var isEraser = false
+
+    /**
+     * 绘制轨迹路径
+     */
+    private var mDrawPath: DrawPath? = null
+
+    /**
+     * 画笔宽度
+     */
+    private var mPaintWidth = 5
+
+    /**
+     * 画笔颜色
+     */
+    private var mPaintColor = 0xFF5052
+
+    /**
+     * 圆形图案半径
+     */
+    private val mCircularP = 150
+    /**
+     * 获取当前画笔样式
+     *
+     * @return
+     */
+    /**
+     * 线型
+     */
+    var canvasStyle = CanvasStyle.FREE_LINE
+        private set
+
+    /**
+     * 当前画布上的每一笔
+     */
+    private var mCurrentPaths: MutableList<DrawPath> = mutableListOf()
+
+    /**
+     * 被撤销的每一笔
+     */
+    private val mDeletePaths: MutableList<DrawPath?>?
+
+    /**
+     * 画布 宽度
+     */
+    private var viewWidth = 10
+
+    /**
+     * 画布 高度
+     */
+    private var viewHeight = 10
+
+    /**
+     * 画笔第一笔点下去的位置
+     */
+    private var mDownX = 0f
+    private var mDownY = 0f
+
+    /**
+     * 画笔移动的位置
+     */
+    private var mMoveX = 0f
+    private var mMoveY = 0f
+
+    /**
+     * 圆点图案移动矩阵
+     */
+    private val mMatrixCircular = Matrix()
+    private var mListener: OnCanvasChangeListener? = null
+
+    /**
+     * 用来回馈上层页面
+     */
+    interface OnCanvasChangeListener {
+        /**
+         * 画布上有绘制操作
+         */
+        fun onDraw()
+    }
+
+    fun setOnCanvasChangeListener(listener: OnCanvasChangeListener?) {
+        mListener = listener
+    }
+
+    //路径对象
+    class DrawPath(
+        startPoint: Point?,
+        var path: Path,
+        var width: Int,
+        var color: Int,
+        var style: CanvasStyle
+    ) {
+        var pointList: MutableList<Point>?
+        var isOver = true
+
+        /**
+         * 外截矩形
+         */
+        var rect: Rect? = null
+
+        /**
+         * @param startPoint 起点
+         * @param path       路径
+         * @param width      宽度
+         * @param color      颜色
+         * @param style      线型
+         */
+        init {
+            pointList = ArrayList()
+            if (startPoint != null) {
+                rect = Rect(startPoint.x, startPoint.y, startPoint.x + 1, startPoint.y + 1)
+            }
+        }
+
+        /**
+         * 获取画笔
+         *
+         * @param paint
+         * @return
+         */
+        fun getPaint(paint: Paint?): Paint? {
+            paint!!.strokeWidth = width.toFloat()
+            paint.color = color
+            return paint
+        }
+    }
+
+    /**
+     * 初始化画布
+     */
+    private fun initCanvas() {
+        //画布大小
+        if (mBitmap != null) {
+            mBitmap!!.recycle()
+        }
+        mBitmap = Bitmap.createBitmap(viewWidth, viewHeight, Bitmap.Config.ARGB_8888)
+        mCanvas = Canvas(mBitmap!!) //所有mCanvas画的东西都被保存在了mBitmap中
+        mPath = Path()
+        mBitmapPaint = Paint(Paint.DITHER_FLAG)
+    }
+
+    /**
+     * 结束折线类型的操作
+     */
+    private fun setPolyLineOver() {
+        if (mCurrentPaths.size > 0) {
+            val path = mCurrentPaths[mCurrentPaths.size - 1]
+            if (path.style == CanvasStyle.RAILWAY_LINE
+                || path.style == CanvasStyle.POLY_LINE
+            ) {
+                //切换类型时,线少于2个点的不要
+                if (path.pointList == null || path.pointList!!.size == 1) {
+                    back() //只有一个点时会遗留一个起始圆点,先移除掉。然后清除
+                    mDeletePaths!!.clear()
+                    if (path === mDrawPath) {
+                        mDrawPath = null
+                        //                        mDrawPath.pointList.clear();
+//                        mPath.reset();
+                    }
+                    return
+                }
+            } else if (path.style == CanvasStyle.GREENLAND_LINE || path.style == CanvasStyle.WATER_LINE || path.style == CanvasStyle.PARKING_LINE) {
+                //切换类型时,面少于3个点的不要
+                if (path.pointList == null || path.pointList!!.size < 4) {
+                    mCurrentPaths.removeAt(mCurrentPaths.size - 1)
+                    initCanvas()
+                }
+                invalidate()
+                return
+            }
+            path.isOver = true
+        }
+    }
+
+    /**
+     * 切换线型
+     *
+     * @param style
+     */
+    fun setStyle(style: CanvasStyle) {
+        canvasStyle = style
+        setPolyLineOver()
+    }
+
+    init {
+        mCurrentPaths = ArrayList()
+        mDeletePaths = ArrayList()
+        mPaint = Paint()
+        mPaint.isAntiAlias = true
+        mPaint.isDither = true
+        mPaint.color = mPaintColor
+        mPaint.style = Paint.Style.STROKE
+        mPaint.strokeJoin = Paint.Join.ROUND
+        mPaint.strokeCap = Paint.Cap.ROUND
+        mPaint.strokeWidth = mPaintWidth.toFloat()
+        mPaintRailway1 = Paint()
+        mPaintRailway1.isAntiAlias = true
+        mPaintRailway1.isDither = true
+        mPaintRailway1.color = -0xff8f40
+        mPaintRailway1.style = Paint.Style.STROKE
+        mPaintRailway1.strokeWidth = 8f
+        mPaintRailway2 = Paint()
+        mPaintRailway2.isAntiAlias = true
+        mPaintRailway2.isDither = true
+        mPaintRailway2.color = Color.WHITE
+        mPaintRailway2.style = Paint.Style.STROKE
+        mPaintRailway2.strokeWidth = 4f
+        mPaintRailway2.pathEffect = pathEffect1
+        mPaintWater = Paint()
+        mPaintWater.isAntiAlias = true
+        mPaintWater.isDither = true
+        mPaintWater.color = -0x543501
+        mPaintWater.style = Paint.Style.FILL
+        mPaintWater.strokeWidth = 4f
+        mPaintWater1 = Paint()
+        mPaintWater1.isAntiAlias = true
+        mPaintWater1.isDither = true
+        mPaintWater1.color = -0xff4fb0
+        mPaintWater1.style = Paint.Style.STROKE
+        mPaintWater1.strokeWidth = 4f
+        mPaintGreenland = Paint()
+        mPaintGreenland.isAntiAlias = true
+        mPaintGreenland.isDither = true
+        mPaintGreenland.color = -0x321c54
+        mPaintGreenland.style = Paint.Style.FILL
+        mPaintGreenland.strokeWidth = 4f
+        mPaintGreenland1 = Paint()
+        mPaintGreenland1.isAntiAlias = true
+        mPaintGreenland1.isDither = true
+        mPaintGreenland1.color = -0xff4fb0
+        mPaintGreenland1.style = Paint.Style.STROKE
+        mPaintGreenland1.strokeWidth = 4f
+        mPaintParking = Paint()
+        mPaintParking.isAntiAlias = true
+        mPaintParking.isDither = true
+        mPaintParking.color = -0x168
+        mPaintParking.style = Paint.Style.FILL
+        mPaintParking.strokeWidth = 4f
+        mPaintParking1 = Paint()
+        mPaintParking1.isAntiAlias = true
+        mPaintParking1.isDither = true
+        mPaintParking1.color = -0x59595a
+        mPaintParking1.style = Paint.Style.STROKE
+        mPaintParking1.strokeWidth = 4f
+        mPaintCircular = Paint()
+        mPaintCircular.style = Paint.Style.FILL
+        mPaintCircular.color = mPaintColor
+        mStarBitmap = BitmapFactory.decodeResource(resources, R.drawable.home_map_center)
+        val a = mContext.obtainStyledAttributes(attrs, R.styleable.CanvasView)
+        isSavePoint = a.getBoolean(R.styleable.CanvasView_isSavePoint, true)
+        a.recycle()
+    }
+
+    /**
+     * 手指移动的处理
+     *
+     * @param x 坐标
+     * @param y 坐标
+     */
+    private fun touch_move_line(x: Float, y: Float) {
+        if (x == mDownX && y == mDownY) return
+        val dx = abs(x - mDownX)
+        val dy = abs(y - mDownY)
+        if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {
+            //增加非空判断
+            if (mPath != null) mPath!!.quadTo(
+                mDownX,
+                mDownY,
+                (x + mDownX) / 2,
+                (y + mDownY) / 2
+            ) //源代码是这样写的,可是我没有弄明白,为什么要这样?
+            mDownX = x
+            mDownY = y
+        }
+        setBuilder(x.toInt(), y.toInt())
+    }
+
+    /**
+     * 多边形起点点下时的处理
+     *
+     * @param x 坐标
+     * @param y 坐标
+     */
+    private fun touch_start_poly(x: Int, y: Int) {
+        if (canvasStyle == CanvasStyle.GREENLAND_LINE || canvasStyle == CanvasStyle.WATER_LINE || canvasStyle == CanvasStyle.PARKING_LINE) {
+            if (mCurrentPaths.size > 0 && mCurrentPaths[0].isOver) {
+                BaseToast.makeText(mContext, "不允许存在多个!", BaseToast.LENGTH_SHORT).show()
+                return
+            }
+            if (mCurrentPaths.size > 0) {
+                val drawPath = mCurrentPaths[mCurrentPaths!!.size - 1]
+                if (drawPath.pointList!!.size > 3) {
+                    if (!GeometryTools.isSimplePolygon(drawPath.pointList, Point(x, y))) {
+                        BaseToast.makeText(mContext, "面不允许自相交图形!", BaseToast.LENGTH_SHORT).show()
+                        return
+                    }
+                }
+            }
+        }
+        if (mDeletePaths != null && mDeletePaths.size > 0) {
+            mDeletePaths.clear()
+        }
+        if (mCurrentPaths.size > 0) {
+            val drawPath = mCurrentPaths[mCurrentPaths.size - 1]
+            if (!drawPath.isOver) {
+                mDrawPath = drawPath
+                if (drawPath.style == CanvasStyle.GREENLAND_LINE || drawPath.style == CanvasStyle.WATER_LINE || drawPath.style == CanvasStyle.PARKING_LINE) {
+                    if (drawPath.pointList!!.size > 1) {
+                        drawPath.pointList!!.removeAt(drawPath.pointList!!.size - 1)
+                    }
+                    setBuilder(x, y)
+                    if (drawPath.pointList!!.size < 3) {
+                        drawPath.path.lineTo(x.toFloat(), y.toFloat())
+                    } else {
+                        initCanvas()
+                        drawPath.path.reset()
+                        for (i in drawPath.pointList!!.indices) {
+                            if (i == 0) {
+                                drawPath.path.moveTo(
+                                    drawPath.pointList!![0].x.toFloat(),
+                                    drawPath.pointList!![0].y.toFloat()
+                                )
+                            } else {
+                                drawPath.path.lineTo(
+                                    drawPath.pointList!![i].x.toFloat(),
+                                    drawPath.pointList!![i].y.toFloat()
+                                )
+                            }
+                        }
+                        drawPath.path.lineTo(
+                            drawPath.pointList!![0].x.toFloat(),
+                            drawPath.pointList!![0].y.toFloat()
+                        )
+                    }
+                    drawPath.pointList!!.add(drawPath.pointList!![0])
+                } else {
+                    drawPath.path.lineTo(x.toFloat(), y.toFloat())
+                    setBuilder(x, y)
+                }
+                if (drawPath.style == CanvasStyle.POLY_LINE) {
+                    mCanvas!!.drawPath(drawPath.path, drawPath.getPaint(mPaint)!!)
+                } else if (drawPath.style == CanvasStyle.RAILWAY_LINE) {
+                    mCanvas!!.drawPath(drawPath.path, mPaintRailway1)
+                    mCanvas!!.drawPath(drawPath.path, mPaintRailway2)
+                } else if (drawPath.style == CanvasStyle.GREENLAND_LINE) {
+                    mCanvas!!.drawPath(drawPath.path, mPaintGreenland)
+                    mCanvas!!.drawPath(drawPath.path, mPaintGreenland1)
+                } else if (drawPath.style == CanvasStyle.WATER_LINE) {
+                    mCanvas!!.drawPath(drawPath.path, mPaintWater)
+                    mCanvas!!.drawPath(drawPath.path, mPaintWater1)
+                } else if (drawPath.style == CanvasStyle.PARKING_LINE) {
+                    mCanvas!!.drawPath(drawPath.path, mPaintParking)
+                    mCanvas!!.drawPath(drawPath.path, mPaintParking1)
+                }
+                invalidate()
+                return
+            }
+        }
+        mPath = Path()
+        mDrawPath = DrawPath(Point(x, y), mPath!!, mPaintWidth, mPaintColor, canvasStyle)
+        mDrawPath!!.isOver = false
+        mPaintCircular.color = mPaintColor
+        mCanvas!!.drawCircle(x.toFloat(), y.toFloat(), 2f, mPaintCircular)
+        mPath!!.moveTo(x.toFloat(), y.toFloat())
+        setBuilder(x, y)
+        if (canvasStyle == CanvasStyle.WATER_LINE || canvasStyle == CanvasStyle.GREENLAND_LINE || canvasStyle == CanvasStyle.PARKING_LINE) {
+            setBuilder(x, y)
+        }
+        mCurrentPaths.add(mDrawPath!!)
+        if (mListener != null) {
+            mListener!!.onDraw()
+        }
+        invalidate()
+        return
+    }
+
+    /**
+     * 手指按下的第一个点处理
+     *
+     * @param x 坐标
+     * @param y 坐标
+     */
+    private fun touch_start(x: Float, y: Float) {
+        mPath!!.reset() //清空path
+        mPath!!.moveTo(x, y)
+        mDownX = x
+        mDownY = y
+        if (canvasStyle != CanvasStyle.ELLIPSE_LINE) setBuilder(x.toInt(), y.toInt())
+        if (mDeletePaths != null && mDeletePaths.size > 0) {
+            mDeletePaths.clear()
+        }
+    }
+
+    /**
+     * 手指离开屏幕时的处理
+     *
+     * @param x 坐标
+     * @param y 坐标
+     */
+    private fun touch_up(x: Float, y: Float) {
+        try {
+            if (canvasStyle == CanvasStyle.POLY_LINE) {
+                val Y = y - mCircularP + mStarBitmap.height / 2
+                mDrawPath!!.width = mPaintWidth
+                mDrawPath!!.color = mPaintColor
+                if (mDrawPath!!.pointList!!.size > 0) {
+                    mDrawPath = mCurrentPaths[mCurrentPaths.size - 1]
+                    mPath = mDrawPath!!.path
+                    mPath!!.lineTo(x, Y)
+                    mCanvas!!.drawPath(mPath!!, mPaint!!)
+                } else {
+                    mCurrentPaths.add(mDrawPath!!)
+                    if (mListener != null) {
+                        mListener!!.onDraw()
+                    }
+                    mPath!!.moveTo(x, Y)
+                    mPaintCircular.color = mPaintColor
+                    mPaintCircular.strokeWidth = 4f
+                    mCanvas!!.drawCircle(x, Y, 4f, mPaintCircular)
+                }
+                setBuilder(x.toInt(), Y.toInt())
+                return
+            } else if (canvasStyle == CanvasStyle.STRAIGHT_LINE) {
+                val Y = y - mCircularP + mStarBitmap.height / 2
+                setBuilder(x.toInt(), Y.toInt())
+                mDrawPath!!.width = mPaintWidth
+                mDrawPath!!.color = mPaintColor
+                if (mDrawPath!!.pointList!!.size > 1) {
+                    mDrawPath!!.isOver = true
+                    mCurrentPaths.add(mDrawPath!!)
+                    if (mListener != null) {
+                        mListener!!.onDraw()
+                    }
+                    mPath!!.lineTo(x, Y)
+                    mCanvas!!.drawPath(mPath!!, mPaint!!)
+                    mPath = null
+                } else {
+                    mPath!!.moveTo(x, Y)
+                    mPaintCircular.color = mPaintColor
+                    mPaintCircular.strokeWidth = 4f
+                    mCanvas!!.drawCircle(x, Y, 4f, mPaintCircular)
+                }
+                return
+            } else if (canvasStyle == CanvasStyle.CIRCULAR_POINT) {
+                mPaintCircular.color = mPaintColor
+                val Y = y - mCircularP + mStarBitmap.height / 2
+                mCanvas!!.drawCircle(x, Y, (mPaintWidth + 5).toFloat(), mPaintCircular)
+                mDrawPath!!.pointList!!.add(Point(x.toInt(), Y.toInt()))
+                mDrawPath!!.rect = Rect(
+                    (x - mPaintWidth - 20).toInt(),
+                    (Y - mPaintWidth - 20).toInt(),
+                    (x + mPaintWidth + 20).toInt(),
+                    (Y + mPaintWidth + 20).toInt()
+                )
+                mCurrentPaths.add(mDrawPath!!)
+                if (mListener != null) {
+                    mListener!!.onDraw()
+                }
+            } else if (canvasStyle == CanvasStyle.RECT_LINE) {
+                if (mDownX == x || mDownY == y) return
+                mPath!!.lineTo(x, mDownY)
+                setBuilder(x.toInt(), mDownY.toInt())
+                mPath!!.lineTo(x, y)
+                setBuilder(x.toInt(), y.toInt())
+                mPath!!.lineTo(mDownX, y)
+                setBuilder(mDownX.toInt(), y.toInt())
+                mPath!!.lineTo(mDownX, mDownY)
+                setBuilder(mDownX.toInt(), mDownY.toInt())
+            } else if (canvasStyle == CanvasStyle.ELLIPSE_LINE) {
+                if (mDownX == x || mDownY == y) {
+                    return
+                }
+                val minX = if (x < mDownX) x else mDownX
+                val maxX = if (x > mDownX) x else mDownX
+                val minY = if (y < mDownY) y else mDownY
+                val maxY = if (y > mDownY) y else mDownY
+                mPath!!.moveTo(minX, minY)
+                mPath!!.addOval(RectF(minX, minY, maxX, maxY), Path.Direction.CW)
+                var a = 0.0
+                val xR = ((maxX - minX) / 2).toDouble()
+                val yR = ((maxY - minY) / 2).toDouble()
+                var tempX = (xR * cos(a) + xR + minX).toInt()
+                val tempY = (yR * sin(a) + yR + minY).toInt()
+                val firstX = tempX
+                setBuilder(tempX, tempY)
+                var bLeft = false
+                var bRight = false
+                while (!bLeft || !bRight) {
+                    a += 0.1
+                    val x1 = (xR * Math.cos(a) + xR + minX).toInt()
+                    val y1 = (yR * Math.sin(a) + yR + minY).toInt()
+                    if (!bLeft && x1 > tempX) {
+                        bLeft = true
+                    }
+                    if (!bRight && bLeft && x1 <= tempX) {
+                        bRight = true
+                        setBuilder(firstX, tempY)
+                    } else {
+                        tempX = x1
+                        setBuilder(x1, y1)
+                    }
+                }
+            } else {
+                if (!contains(
+                        mDownX.toInt(),
+                        mDownY.toInt(),
+                        x.toInt(),
+                        y.toInt(),
+                        ChouXiBanJing
+                    )
+                ) {
+                    mPath!!.lineTo(x, y)
+                    setBuilder(x.toInt(), y.toInt())
+                }
+                if (canvasStyle == CanvasStyle.GREENLAND_LINE || canvasStyle == CanvasStyle.WATER_LINE || canvasStyle == CanvasStyle.PARKING_LINE) {
+                    val point = mDrawPath!!.pointList!![0]
+                    mPath!!.lineTo(point.x.toFloat(), point.y.toFloat())
+                    setBuilder(point.x, point.y)
+                }
+            }
+            if (mDrawPath!!.pointList!!.size > 1) {
+                if (canvasStyle == CanvasStyle.RAILWAY_LINE) {
+                    mCanvas!!.drawPath(mPath!!, mPaintRailway1)
+                    mCanvas!!.drawPath(mPath!!, mPaintRailway2)
+                } else if (canvasStyle == CanvasStyle.GREENLAND_LINE) {
+                    mCanvas!!.drawPath(mPath!!, mPaintGreenland)
+                    mCanvas!!.drawPath(mPath!!, mPaintGreenland1)
+                } else if (canvasStyle == CanvasStyle.WATER_LINE) {
+                    mCanvas!!.drawPath(mPath!!, mPaintWater)
+                    mCanvas!!.drawPath(mPath!!, mPaintWater1)
+                } else if (canvasStyle == CanvasStyle.PARKING_LINE) {
+                    mCanvas!!.drawPath(mPath!!, mPaintParking)
+                    mCanvas!!.drawPath(mPath!!, mPaintParking1)
+                } else {
+                    mCanvas!!.drawPath(mPath!!, mPaint!!)
+                }
+                mCurrentPaths.add(mDrawPath!!)
+                if (mListener != null) {
+                    mListener!!.onDraw()
+                }
+            }
+            mPath = null
+        } catch (e: Exception) {
+        }
+    }
+
+    /**
+     * 记录每一个点,并计算外截矩形
+     *
+     * @param x
+     * @param y
+     */
+    private fun setBuilder(x: Int, y: Int) {
+        if (!isSavePoint || mDrawPath == null) return
+        if (mDrawPath!!.pointList == null) mDrawPath!!.pointList = ArrayList()
+        if (mDrawPath!!.rect == null) {
+            mDrawPath!!.rect = Rect(x, y, x + 1, y + 1)
+        }
+        if (x < mDrawPath!!.rect!!.left) {
+            mDrawPath!!.rect!!.left = x
+        } else if (x > mDrawPath!!.rect!!.right) {
+            mDrawPath!!.rect!!.right = x
+        }
+        if (y < mDrawPath!!.rect!!.top) {
+            mDrawPath!!.rect!!.top = y
+        } else if (y > mDrawPath!!.rect!!.bottom) {
+            mDrawPath!!.rect!!.bottom = y
+        }
+        mDrawPath!!.pointList!!.add(Point(x, y))
+    }
+
+    /**
+     * 记录手指滑动中的坐标
+     *
+     * @param x
+     * @param y
+     */
+    private fun touch_move(x: Float, y: Float) {
+        mMoveX = x
+        mMoveY = y
+    }
+
+    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
+        super.onSizeChanged(w, h, oldw, oldh)
+        viewWidth = w
+        viewHeight = h
+        initCanvas()
+        if (mCurrentPaths.isNotEmpty()) {
+            for (dp in mCurrentPaths) {
+                if (dp.style == CanvasStyle.RAILWAY_LINE) {
+                    mCanvas!!.drawPath(dp.path, mPaintRailway1)
+                    mCanvas!!.drawPath(dp.path, mPaintRailway2)
+                } else if (dp.style == CanvasStyle.GREENLAND_LINE) {
+                    mCanvas!!.drawPath(dp.path, mPaintGreenland)
+                    mCanvas!!.drawPath(dp.path, mPaintGreenland1)
+                } else if (dp.style == CanvasStyle.WATER_LINE) {
+                    mCanvas!!.drawPath(dp.path, mPaintWater)
+                    mCanvas!!.drawPath(mPath!!, mPaintWater1)
+                } else if (dp.style == CanvasStyle.PARKING_LINE) {
+                    mCanvas!!.drawPath(dp.path, mPaintParking)
+                    mCanvas!!.drawPath(mPath!!, mPaintParking1)
+                } else {
+                    mCanvas!!.drawPath(dp.path, dp.getPaint(mPaint)!!)
+                }
+            }
+        }
+    }
+
+    /**
+     * 撤销上一步
+     */
+    fun back(): Boolean {
+        if (mCurrentPaths.size > 0) {
+            //调用初始化画布函数以清空画布
+            initCanvas()
+            //将路径保存列表中的最后一个元素删除 ,并将其保存在路径删除列表中
+            val drawPath = mCurrentPaths[mCurrentPaths.size - 1]
+            //如果是正在绘制的折线
+            if ((drawPath.style == CanvasStyle.POLY_LINE
+                        || drawPath.style == CanvasStyle.RAILWAY_LINE)
+                && !drawPath.isOver
+            ) {
+                val drawPathCopy: DrawPath?
+                if (mDeletePaths!!.size == 0 || mDeletePaths[mDeletePaths.size - 1]!!.style != drawPath.style || mDeletePaths[mDeletePaths.size - 1]!!.isOver) {
+                    drawPathCopy = DrawPath(
+                        null,
+                        drawPath.path,
+                        drawPath.width,
+                        drawPath.color,
+                        drawPath.style
+                    )
+                    drawPathCopy.rect = drawPath.rect
+                    drawPathCopy.isOver = false
+                    mDeletePaths.add(drawPathCopy)
+                } else {
+                    drawPathCopy = mDeletePaths[mDeletePaths.size - 1]
+                }
+                val point = drawPath.pointList!![drawPath.pointList!!.size - 1]
+                drawPathCopy!!.pointList!!.add(point)
+                if (drawPath.pointList!!.size == 1) {
+                    mCurrentPaths.removeAt(mCurrentPaths.size - 1)
+                } else {
+                    drawPath.pointList!!.remove(point)
+                }
+            } else if ((drawPath.style == CanvasStyle.WATER_LINE || drawPath.style == CanvasStyle.GREENLAND_LINE || drawPath.style == CanvasStyle.PARKING_LINE)
+                && !drawPath.isOver
+            ) {
+                val drawPathCopy: DrawPath?
+                if (mDeletePaths!!.size == 0 || mDeletePaths[mDeletePaths.size - 1]!!.style != drawPath.style || mDeletePaths[mDeletePaths.size - 1]!!.isOver) {
+                    drawPathCopy = DrawPath(
+                        null,
+                        drawPath.path,
+                        drawPath.width,
+                        drawPath.color,
+                        drawPath.style
+                    )
+                    drawPathCopy.rect = drawPath.rect
+                    drawPathCopy.isOver = false
+                    mDeletePaths.add(drawPathCopy)
+                } else {
+                    drawPathCopy = mDeletePaths[mDeletePaths.size - 1]
+                }
+                val point = drawPath.pointList!![drawPath.pointList!!.size - 2]
+                drawPathCopy!!.pointList!!.add(point)
+                if (drawPath.pointList!!.size < 3) {
+                    mCurrentPaths.removeAt(mCurrentPaths.size - 1)
+                } else {
+                    drawPath.pointList!!.removeAt(drawPath.pointList!!.size - 1)
+                    drawPath.pointList!!.remove(point)
+                    drawPath.pointList!!.add(drawPath.pointList!![0])
+                }
+            } else {
+                mDeletePaths!!.add(drawPath)
+                mCurrentPaths.removeAt(mCurrentPaths.size - 1)
+            }
+            if (mCurrentPaths.isNotEmpty()) {
+                //将路径保存列表中的路径重绘在画布上
+                for (dp in mCurrentPaths) {
+                    if (dp.style == CanvasStyle.POLY_LINE) {
+                        dp.path.reset()
+                        for (i in dp.pointList!!.indices) {
+                            val point = dp.pointList!![i]
+                            mPaintCircular.color = dp.color
+                            if (i == 0) {
+                                mCanvas!!.drawCircle(
+                                    point.x.toFloat(),
+                                    point.y.toFloat(),
+                                    2f,
+                                    mPaintCircular
+                                )
+                                dp.path.moveTo(point.x.toFloat(), point.y.toFloat())
+                            } else {
+                                dp.path.lineTo(point.x.toFloat(), point.y.toFloat())
+                            }
+                        }
+                        mCanvas!!.drawPath(dp.path, dp.getPaint(mPaint)!!)
+                    } else if (dp.style == CanvasStyle.RAILWAY_LINE) {
+                        dp.path.reset()
+                        for (i in dp.pointList!!.indices) {
+                            val point = dp.pointList!![i]
+                            if (i == 0) {
+                                mCanvas!!.drawCircle(
+                                    point.x.toFloat(),
+                                    point.y.toFloat(),
+                                    2f,
+                                    mPaintCircular
+                                )
+                                dp.path.moveTo(point.x.toFloat(), point.y.toFloat())
+                            } else {
+                                dp.path.lineTo(point.x.toFloat(), point.y.toFloat())
+                            }
+                        }
+                        mCanvas!!.drawPath(dp.path, mPaintRailway1)
+                        mCanvas!!.drawPath(dp.path, mPaintRailway2)
+                    } else if (dp.style == CanvasStyle.GREENLAND_LINE) {
+                        dp.path.reset()
+                        for (i in dp.pointList!!.indices) {
+                            val point = dp.pointList!![i]
+                            if (i == 0) {
+                                mCanvas!!.drawCircle(
+                                    point.x.toFloat(),
+                                    point.y.toFloat(),
+                                    2f,
+                                    mPaintCircular
+                                )
+                                dp.path.moveTo(point.x.toFloat(), point.y.toFloat())
+                            } else {
+                                dp.path.lineTo(point.x.toFloat(), point.y.toFloat())
+                            }
+                        }
+                        mCanvas!!.drawPath(dp.path, mPaintGreenland)
+                        mCanvas!!.drawPath(dp.path, mPaintGreenland1)
+                    } else if (dp.style == CanvasStyle.WATER_LINE) {
+                        dp.path.reset()
+                        for (i in dp.pointList!!.indices) {
+                            val point = dp.pointList!![i]
+                            if (i == 0) {
+                                mCanvas!!.drawCircle(
+                                    point.x.toFloat(),
+                                    point.y.toFloat(),
+                                    2f,
+                                    mPaintCircular
+                                )
+                                dp.path.moveTo(point.x.toFloat(), point.y.toFloat())
+                            } else {
+                                dp.path.lineTo(point.x.toFloat(), point.y.toFloat())
+                            }
+                        }
+                        mCanvas!!.drawPath(dp.path, mPaintWater)
+                        mCanvas!!.drawPath(dp.path, mPaintWater1)
+                    } else if (dp.style == CanvasStyle.PARKING_LINE) {
+                        dp.path.reset()
+                        for (i in dp.pointList!!.indices) {
+                            val point = dp.pointList!![i]
+                            if (i == 0) {
+                                mCanvas!!.drawCircle(
+                                    point.x.toFloat(),
+                                    point.y.toFloat(),
+                                    2f,
+                                    mPaintCircular
+                                )
+                                dp.path.moveTo(point.x.toFloat(), point.y.toFloat())
+                            } else {
+                                dp.path.lineTo(point.x.toFloat(), point.y.toFloat())
+                            }
+                        }
+                        mCanvas!!.drawPath(dp.path, mPaintParking)
+                        mCanvas!!.drawPath(dp.path, mPaintParking1)
+                    } else if (dp.style == CanvasStyle.CIRCULAR_POINT) {
+                        mPaintCircular.color = dp.color
+                        mCanvas!!.drawCircle(
+                            dp.pointList!![0].x.toFloat(),
+                            dp.pointList!![0].y.toFloat(),
+                            dp.width.toFloat(),
+                            mPaintCircular
+                        )
+                    } else {
+                        mCanvas!!.drawPath(dp.path, dp.getPaint(mPaint)!!)
+                    }
+                }
+            }
+            invalidate() // 刷新
+        }
+        if (mCurrentPaths.size == 0) {
+            mDrawPath = null
+        }
+        return mCurrentPaths.size != 0
+    }
+
+    /**
+     * 恢复撤销的上一步
+     */
+    fun forward() {
+        if (mDeletePaths!!.size > 0) {
+            //将删除的路径列表中的最后一个,也就是最顶端路径取出(栈),并加入路径保存列表中
+            val dp = mDeletePaths[mDeletePaths.size - 1]
+            if ((dp!!.style == CanvasStyle.POLY_LINE
+                        || dp.style == CanvasStyle.RAILWAY_LINE)
+                && !dp.isOver
+            ) {
+                val dp2: DrawPath?
+                if (mCurrentPaths.size > 0 && mCurrentPaths[mCurrentPaths.size - 1].style == dp.style && !mCurrentPaths[mCurrentPaths.size - 1].isOver) {
+                    dp2 = mCurrentPaths[mCurrentPaths.size - 1]
+                } else {
+                    dp2 = DrawPath(null, dp.path, dp.width, dp.color, dp.style)
+                    dp2.isOver = false
+                    dp2.rect = dp.rect
+                    mCurrentPaths.add(dp2)
+                    if (mListener != null) {
+                        mListener!!.onDraw()
+                    }
+                }
+                val point = dp.pointList!![dp.pointList!!.size - 1]
+                dp2.pointList!!.add(point)
+                dp.pointList!!.remove(point)
+            } else if ((dp.style == CanvasStyle.WATER_LINE || dp.style == CanvasStyle.GREENLAND_LINE || dp.style == CanvasStyle.PARKING_LINE)
+                && !dp.isOver
+            ) {
+                val dp2: DrawPath?
+                if (mCurrentPaths.size > 0 && mCurrentPaths[mCurrentPaths.size - 1].style == dp.style && !mCurrentPaths[mCurrentPaths.size - 1].isOver) {
+                    dp2 = mCurrentPaths[mCurrentPaths.size - 1]
+                } else {
+                    dp2 = DrawPath(null, dp.path, dp.width, dp.color, dp.style)
+                    dp2.isOver = false
+                    dp2.rect = dp.rect
+                    mCurrentPaths.add(dp2)
+                    if (mListener != null) {
+                        mListener!!.onDraw()
+                    }
+                }
+                val point = dp.pointList!![dp.pointList!!.size - 1]
+                if (dp2.pointList!!.size > 1) {
+                    dp2.pointList!!.removeAt(dp2.pointList!!.size - 1)
+                }
+                dp2.pointList!!.add(point)
+                dp2.pointList!!.add(dp2.pointList!![0])
+                dp.pointList!!.remove(point)
+            } else {
+                mCurrentPaths.add(dp)
+                if (mListener != null) {
+                    mListener!!.onDraw()
+                }
+            }
+            //将取出的路径重绘在画布上
+            if (dp.style == CanvasStyle.POLY_LINE) {
+                val dp2 = mCurrentPaths[mCurrentPaths.size - 1]
+                dp2.path.reset()
+                if (dp2.pointList != null && dp2.pointList!!.size > 0) {
+                    for (i in dp2.pointList!!.indices) {
+                        val point = dp2.pointList!![i]
+                        mPaintCircular.color = dp2.color
+                        if (i == 0) {
+                            mCanvas!!.drawCircle(
+                                point.x.toFloat(),
+                                point.y.toFloat(),
+                                2f,
+                                mPaintCircular
+                            )
+                            dp2.path.moveTo(point.x.toFloat(), point.y.toFloat())
+                        } else {
+                            dp2.path.lineTo(point.x.toFloat(), point.y.toFloat())
+                        }
+                    }
+                }
+                mCanvas!!.drawPath(dp2.path, dp2.getPaint(mPaint)!!)
+            } else if (dp.style == CanvasStyle.RAILWAY_LINE) {
+                val dp2 = mCurrentPaths[mCurrentPaths.size - 1]
+                dp2.path.reset()
+                if (dp2.pointList != null && dp2.pointList!!.size > 0) {
+                    for (i in dp2.pointList!!.indices) {
+                        val point = dp2.pointList!![i]
+                        if (i == 0) {
+                            dp2.path.moveTo(point.x.toFloat(), point.y.toFloat())
+                        } else {
+                            dp2.path.lineTo(point.x.toFloat(), point.y.toFloat())
+                        }
+                    }
+                }
+                mCanvas!!.drawPath(dp2.path, mPaintRailway1)
+                mCanvas!!.drawPath(dp2.path, mPaintRailway2)
+            } else if (dp.style == CanvasStyle.GREENLAND_LINE) {
+                val dp2 = mCurrentPaths[mCurrentPaths.size - 1]
+                initCanvas()
+                dp2.path.reset()
+                if (dp2.pointList != null && dp2.pointList!!.size > 0) {
+                    for (i in dp2.pointList!!.indices) {
+                        val point = dp2.pointList!![i]
+                        if (i == 0) {
+                            mCanvas!!.drawCircle(
+                                point.x.toFloat(),
+                                point.y.toFloat(),
+                                2f,
+                                mPaintCircular
+                            )
+                            dp2.path.moveTo(point.x.toFloat(), point.y.toFloat())
+                        } else {
+                            dp2.path.lineTo(point.x.toFloat(), point.y.toFloat())
+                        }
+                    }
+                }
+                mCanvas!!.drawPath(dp2.path, mPaintGreenland)
+                mCanvas!!.drawPath(dp2.path, mPaintGreenland1)
+            } else if (dp.style == CanvasStyle.WATER_LINE) {
+                val dp2 = mCurrentPaths[mCurrentPaths.size - 1]
+                initCanvas()
+                dp2.path.reset()
+                if (dp2.pointList != null && dp2.pointList!!.size > 0) {
+                    for (i in dp2.pointList!!.indices) {
+                        val point = dp2.pointList!![i]
+                        if (i == 0) {
+                            mCanvas!!.drawCircle(
+                                point.x.toFloat(),
+                                point.y.toFloat(),
+                                2f,
+                                mPaintCircular
+                            )
+                            dp2.path.moveTo(point.x.toFloat(), point.y.toFloat())
+                        } else {
+                            dp2.path.lineTo(point.x.toFloat(), point.y.toFloat())
+                        }
+                    }
+                }
+                mCanvas!!.drawPath(dp2.path, mPaintWater)
+                mCanvas!!.drawPath(dp2.path, mPaintWater1)
+            } else if (dp.style == CanvasStyle.PARKING_LINE) {
+                val dp2 = mCurrentPaths[mCurrentPaths.size - 1]
+                initCanvas()
+                dp2!!.path.reset()
+                if (dp2.pointList != null && dp2.pointList!!.size > 0) {
+                    for (i in dp2.pointList!!.indices) {
+                        val point = dp2.pointList!![i]
+                        if (i == 0) {
+                            mCanvas!!.drawCircle(
+                                point.x.toFloat(),
+                                point.y.toFloat(),
+                                2f,
+                                mPaintCircular
+                            )
+                            dp2.path.moveTo(point.x.toFloat(), point.y.toFloat())
+                        } else {
+                            dp2.path.lineTo(point.x.toFloat(), point.y.toFloat())
+                        }
+                    }
+                }
+                mCanvas!!.drawPath(dp2.path, mPaintParking)
+                mCanvas!!.drawPath(dp2.path, mPaintParking1)
+            } else if (dp.style == CanvasStyle.CIRCULAR_POINT) {
+                mPaintCircular.color = dp.color
+                mCanvas!!.drawCircle(
+                    dp.pointList!![0].x.toFloat(),
+                    dp.pointList!![0].y.toFloat(),
+                    dp.width.toFloat(),
+                    mPaintCircular
+                )
+            } else {
+                mCanvas!!.drawPath(dp.path, dp.getPaint(mPaint)!!)
+            }
+            //将该路径从删除的路径列表中去除
+            if ((dp.style == CanvasStyle.POLY_LINE
+                        || dp.style == CanvasStyle.RAILWAY_LINE)
+                && !dp.isOver
+            ) {
+                if (dp.pointList!!.size == 0) {
+                    mDeletePaths.remove(dp)
+                }
+            } else if ((dp.style == CanvasStyle.WATER_LINE || dp.style == CanvasStyle.GREENLAND_LINE || dp.style == CanvasStyle.PARKING_LINE)
+                && !dp.isOver
+            ) {
+                if (dp.pointList!!.size == 0) {
+                    mDeletePaths.remove(dp)
+                }
+            } else {
+                mDeletePaths.removeAt(mDeletePaths.size - 1)
+            }
+            invalidate()
+        }
+    }
+
+    /*
+         * 清空的主要思想就是初始化画布
+         * 将保存路径的两个List清空
+         * */
+    fun removeAllPaint() {
+        //调用初始化画布函数以清空画布
+        initCanvas()
+        invalidate() //刷新
+        mPath = null
+        mDrawPath = null
+        mCurrentPaths.clear()
+        mDeletePaths!!.clear()
+    }
+
+    /**
+     * 设置画笔粗细
+     *
+     * @param paintWidth
+     */
+    fun setPaintWidth(paintWidth: Int) {
+        mPaintWidth = paintWidth
+        if (mPaint != null) {
+            mPaint.strokeWidth = mPaintWidth.toFloat()
+        }
+    }
+
+    /**
+     * 设置画笔颜色
+     *
+     * @param color
+     */
+    fun setPaintColor(color: Int) {
+        mPaintColor = color
+        if (mPaint != null) {
+            mPaint.color = mPaintColor
+        }
+    }
+
+    /**
+     * 将图形数据绘制在画布上
+     *
+     * @param value
+     */
+    fun setDrawPathList(value: MutableList<DrawPath>) {
+        mPath = null
+        mCurrentPaths = value
+        if (mListener != null) {
+            mListener!!.onDraw()
+        }
+        mDeletePaths!!.clear()
+        if (mCanvas != null) {
+            if (mCurrentPaths.size > 0) {
+                for (dp in mCurrentPaths) {
+                    if (dp.style == CanvasStyle.RAILWAY_LINE) {
+                        dp.path.reset()
+                        for (i in dp.pointList!!.indices) {
+                            if (i == 0) {
+                                dp.path.moveTo(
+                                    dp.pointList!![i].x.toFloat(),
+                                    dp.pointList!![i].y.toFloat()
+                                )
+                            } else {
+                                dp.path.lineTo(
+                                    dp.pointList!![i].x.toFloat(),
+                                    dp.pointList!![i].y.toFloat()
+                                )
+                            }
+                        }
+                        mCanvas!!.drawPath(dp.path, mPaintRailway1)
+                        mCanvas!!.drawPath(dp.path, mPaintRailway2)
+                    } else if (dp.style == CanvasStyle.GREENLAND_LINE) {
+                        mCanvas!!.drawPath(dp.path, mPaintGreenland)
+                        mCanvas!!.drawPath(dp.path, mPaintGreenland1)
+                    } else if (dp.style == CanvasStyle.WATER_LINE) {
+                        mCanvas!!.drawPath(dp.path, mPaintWater)
+                        mCanvas!!.drawPath(dp.path, mPaintWater1)
+                    } else if (dp.style == CanvasStyle.PARKING_LINE) {
+                        mCanvas!!.drawPath(dp.path, mPaintParking)
+                        mCanvas!!.drawPath(dp.path, mPaintParking1)
+                    } else if (dp.style == CanvasStyle.CIRCULAR_POINT) {
+                        mPaintCircular.color = dp.color
+                        mCanvas!!.drawCircle(
+                            dp.pointList!![0].x.toFloat(),
+                            dp.pointList!![0].y.toFloat(),
+                            dp.width.toFloat(),
+                            mPaintCircular
+                        )
+                    } else if (dp.style == CanvasStyle.POLY_LINE) {
+                        mPaintCircular.color = dp.color
+                        dp.path.reset()
+                        for (i in dp.pointList!!.indices) {
+                            if (i == 0) {
+                                mCanvas!!.drawCircle(
+                                    dp.pointList!![i].x.toFloat(),
+                                    dp.pointList!![i].y.toFloat(),
+                                    2f,
+                                    mPaintCircular
+                                )
+                                dp.path.moveTo(
+                                    dp.pointList!![i].x.toFloat(),
+                                    dp.pointList!![i].y.toFloat()
+                                )
+                            } else {
+                                dp.path.lineTo(
+                                    dp.pointList!![i].x.toFloat(),
+                                    dp.pointList!![i].y.toFloat()
+                                )
+                            }
+                        }
+                        mCanvas!!.drawPath(dp.path, dp.getPaint(mPaint)!!)
+                    } else {
+                        mCanvas!!.drawPath(dp.path, dp.getPaint(mPaint)!!)
+                    }
+                    canvasStyle = dp.style
+                }
+            }
+            mPaint!!.color = mPaintColor
+        }
+        invalidate() // 刷新
+    }
+
+    override fun onDraw(canvas: Canvas) {
+        super.onDraw(canvas)
+        canvas.drawBitmap(mBitmap!!, 0f, 0f, mBitmapPaint) //显示旧的画布
+        if (mPath != null) {
+            // 实时的显示
+            if (isMove) {
+                if (canvasStyle == CanvasStyle.CIRCULAR_POINT) {
+                    mMatrixCircular.reset()
+                    mMatrixCircular.postTranslate(
+                        mMoveX - mStarBitmap.width / 2,
+                        mMoveY - mCircularP
+                    )
+                    canvas.drawBitmap(mStarBitmap, mMatrixCircular, null)
+                } else if (canvasStyle == CanvasStyle.STRAIGHT_LINE) {
+                    if (mDrawPath != null && mDrawPath!!.pointList!!.size > 0) {
+                        val x = mDrawPath!!.pointList!![0].x
+                        val y = mDrawPath!!.pointList!![0].y
+                        val Y2 = mMoveY - mCircularP + mStarBitmap.height / 2
+                        canvas.drawLine(x.toFloat(), y.toFloat(), mMoveX, Y2, mPaint!!)
+                    }
+                    mMatrixCircular.reset()
+                    mMatrixCircular.postTranslate(
+                        mMoveX - mStarBitmap.width / 2,
+                        mMoveY - mCircularP
+                    )
+                    canvas.drawBitmap(mStarBitmap, mMatrixCircular, null)
+                } else if (canvasStyle == CanvasStyle.POLY_LINE) {
+                    if (mDrawPath != null && mDrawPath!!.pointList!!.size > 0) {
+                        val x = mDrawPath!!.pointList!![mDrawPath!!.pointList!!.size - 1].x
+                        val y = mDrawPath!!.pointList!![mDrawPath!!.pointList!!.size - 1].y
+                        val Y2 = mMoveY - mCircularP + mStarBitmap.height / 2
+                        canvas.drawLine(x.toFloat(), y.toFloat(), mMoveX, Y2, mPaint!!)
+                    }
+                    mMatrixCircular.reset()
+                    mMatrixCircular.postTranslate(
+                        mMoveX - mStarBitmap.width / 2,
+                        mMoveY - mCircularP
+                    )
+                    canvas.drawBitmap(mStarBitmap, mMatrixCircular, null)
+                } else if (canvasStyle == CanvasStyle.RECT_LINE) {
+                    if (mDownX > mMoveX && mDownY > mMoveY) {
+                        canvas.drawRect(mMoveX, mMoveY, mDownX, mDownY, mPaint!!)
+                    } else if (mDownX < mMoveX && mDownY > mMoveY) {
+                        canvas.drawRect(mDownX, mMoveY, mMoveX, mDownY, mPaint!!)
+                    } else if (mDownX > mMoveX && mDownY < mMoveY) {
+                        canvas.drawRect(mMoveX, mDownY, mDownX, mMoveY, mPaint!!)
+                    } else {
+                        canvas.drawRect(mDownX, mDownY, mMoveX, mMoveY, mPaint!!)
+                    }
+                } else if (canvasStyle == CanvasStyle.ELLIPSE_LINE) {
+                    if (mDownX > mMoveX && mDownY > mMoveY) {
+                        canvas.drawOval(RectF(mMoveX, mMoveY, mDownX, mDownY), mPaint!!)
+                    } else if (mDownX < mMoveX && mDownY > mMoveY) {
+                        canvas.drawOval(RectF(mDownX, mMoveY, mMoveX, mDownY), mPaint!!)
+                    } else if (mDownX > mMoveX && mDownY < mMoveY) {
+                        canvas.drawOval(RectF(mMoveX, mDownY, mDownX, mMoveY), mPaint!!)
+                    } else {
+                        canvas.drawOval(RectF(mDownX, mDownY, mMoveX, mMoveY), mPaint!!)
+                    }
+                } else {
+                    if (canvasStyle == CanvasStyle.RAILWAY_LINE) {
+                        canvas.drawPath(mPath!!, mPaintRailway1)
+                        canvas.drawPath(mPath!!, mPaintRailway2)
+                    } else if (canvasStyle == CanvasStyle.GREENLAND_LINE) {
+                        canvas.drawPath(mPath!!, mPaintGreenland1)
+                    } else if (canvasStyle == CanvasStyle.WATER_LINE) {
+                        canvas.drawPath(mPath!!, mPaintWater1)
+                    } else if (canvasStyle == CanvasStyle.PARKING_LINE) {
+                        canvas.drawPath(mPath!!, mPaintParking1)
+                    } else {
+                        canvas.drawPath(mPath!!, mPaint!!)
+                    }
+                }
+            } else {
+
+
+//                if (mStyle == CanvasStyle.RAILWAY_LINE) {
+//                    canvas.drawPath(mPath, mPaintRailway1);
+//                    canvas.drawPath(mPath, mPaintRailway2);
+//                } else {
+//                    canvas.drawPath(mPath, mPaint);
+//                }
+            }
+        }
+    }
+
+    override fun onTouchEvent(event: MotionEvent): Boolean {
+        val x = event.x
+        val y = event.y
+        when (event.action) {
+            MotionEvent.ACTION_DOWN -> if (isEraser) {
+                clearLine(x.toInt(), y.toInt())
+            } else {
+//                    if (mCurrentPaths != null && mCurrentPaths.size() > 0) {
+//                        for (DrawPath dp : mCurrentPaths) {
+//                            if (dp.style == CanvasStyle.GREENLAND_LINE || dp.style == CanvasStyle.WATER_LINE) {
+//                                return true;
+//                            }
+//                        }
+//                    }
+                isMove = false
+                if (canvasStyle == CanvasStyle.RAILWAY_LINE || canvasStyle == CanvasStyle.GREENLAND_LINE || canvasStyle == CanvasStyle.WATER_LINE || canvasStyle == CanvasStyle.PARKING_LINE) {
+                    touch_start_poly(x.toInt(), y.toInt())
+                    return true
+                } else if (canvasStyle == CanvasStyle.CIRCULAR_POINT) {
+                    mPath = Path()
+                    mDrawPath = DrawPath(null, mPath!!, mPaintWidth + 5, mPaintColor, canvasStyle)
+                    return true
+                } else if (canvasStyle == CanvasStyle.POLY_LINE) {
+                    if (mDeletePaths != null && mDeletePaths.size > 0) {
+                        mDeletePaths.clear()
+                    }
+                    if (mDrawPath != null && mDrawPath!!.style == CanvasStyle.POLY_LINE && !mDrawPath!!.isOver) {
+                        return true
+                    }
+                    mPath = Path()
+                    mDrawPath = DrawPath(
+                        Point(x.toInt(), y.toInt()),
+                        mPath!!,
+                        mPaintWidth,
+                        mPaintColor,
+                        canvasStyle
+                    )
+                    mDrawPath!!.isOver = false
+                    return true
+                } else if (canvasStyle == CanvasStyle.STRAIGHT_LINE) {
+                    if (mDeletePaths != null && mDeletePaths.size > 0) {
+                        mDeletePaths.clear()
+                    }
+                    if (mDrawPath != null && mDrawPath!!.style == CanvasStyle.STRAIGHT_LINE && !mDrawPath!!.isOver) {
+                        return true
+                    }
+                    mPath = Path()
+                    mDrawPath = DrawPath(
+                        Point(x.toInt(), y.toInt()),
+                        mPath!!,
+                        mPaintWidth,
+                        mPaintColor,
+                        canvasStyle
+                    )
+                    mDrawPath!!.isOver = false
+                    return true
+                }
+                mPath = Path()
+                mDrawPath = DrawPath(
+                    Point(x.toInt(), y.toInt()),
+                    mPath!!,
+                    mPaintWidth,
+                    mPaintColor,
+                    canvasStyle
+                )
+                touch_start(x, y)
+            }
+            MotionEvent.ACTION_MOVE -> {
+                if (isEraser) {
+                    return true
+                }
+                if (canvasStyle == CanvasStyle.RAILWAY_LINE || canvasStyle == CanvasStyle.GREENLAND_LINE || canvasStyle == CanvasStyle.WATER_LINE || canvasStyle == CanvasStyle.PARKING_LINE) return true
+
+//                if (mCurrentPaths != null && mCurrentPaths.size() > 0) {
+//                    for (DrawPath dp : mCurrentPaths) {
+//                        if (dp.style == CanvasStyle.GREENLAND_LINE || dp.style == CanvasStyle.WATER_LINE) {
+//                            return true;
+//                        }
+//                    }
+//                }
+                isMove = true
+                if (x == mDownX && y == mDownY) return false
+                if (canvasStyle == CanvasStyle.POLY_LINE || canvasStyle == CanvasStyle.STRAIGHT_LINE || canvasStyle == CanvasStyle.RECT_LINE || canvasStyle == CanvasStyle.ELLIPSE_LINE || canvasStyle == CanvasStyle.CIRCULAR_POINT) {
+                    touch_move(x, y)
+                } else {
+                    if (contains(
+                            mDownX.toInt(),
+                            mDownY.toInt(),
+                            x.toInt(),
+                            y.toInt(),
+                            ChouXiBanJing
+                        )
+                    ) {
+                        return true
+                    }
+                    touch_move_line(x, y)
+                }
+                invalidate()
+            }
+            MotionEvent.ACTION_UP -> {
+                if (isEraser) {
+                    return true
+                }
+                if (canvasStyle == CanvasStyle.RAILWAY_LINE || canvasStyle == CanvasStyle.GREENLAND_LINE || canvasStyle == CanvasStyle.WATER_LINE || canvasStyle == CanvasStyle.PARKING_LINE) return true
+                //                if (mCurrentPaths != null && mCurrentPaths.size() > 0) {
+//                    for (DrawPath dp : mCurrentPaths) {
+//                        if (dp.style == CanvasStyle.GREENLAND_LINE || dp.style == CanvasStyle.WATER_LINE) {
+//                            return true;
+//                        }
+//                    }
+//                }
+                touch_up(x, y)
+                invalidate()
+                isMove = false
+            }
+        }
+        return true
+    }
+
+    /**
+     * 橡皮擦捕捉清除
+     *
+     * @param x
+     * @param y
+     */
+    private fun clearLine(x: Int, y: Int) {
+        if (mCurrentPaths.size == 0) return
+        val rect = Rect(x - EraserWight, y - EraserWight, x + EraserWight, y + EraserWight)
+        for (i in mCurrentPaths.size - 1 downTo -1 + 1) {
+            val path = mCurrentPaths[i]
+            if (path.rect != null && path.rect!!.intersects(
+                    rect.left,
+                    rect.top,
+                    rect.right,
+                    rect.bottom
+                )
+            ) {
+                if (LineIntersectRect(path.pointList, rect)) {
+                    initCanvas()
+                    mDeletePaths!!.add(path)
+                    mCurrentPaths.remove(path)
+                    for (dp in mCurrentPaths) {
+                        if (dp.style == CanvasStyle.RAILWAY_LINE) {
+                            dp.path.reset()
+                            if (dp.pointList != null && dp.pointList!!.size > 0) {
+                                for (k in dp.pointList!!.indices) {
+                                    if (k == 0) {
+                                        dp.path.moveTo(
+                                            dp.pointList!![k].x.toFloat(),
+                                            dp.pointList!![k].y.toFloat()
+                                        )
+                                    } else {
+                                        dp.path.lineTo(
+                                            dp.pointList!![k].x.toFloat(),
+                                            dp.pointList!![k].y.toFloat()
+                                        )
+                                    }
+                                }
+                            }
+                            mCanvas!!.drawPath(dp.path, mPaintRailway1)
+                            mCanvas!!.drawPath(dp.path, mPaintRailway2)
+                        } else if (dp.style == CanvasStyle.GREENLAND_LINE) {
+                            mCanvas!!.drawPath(dp.path, mPaintGreenland)
+                            mCanvas!!.drawPath(dp.path, mPaintGreenland1)
+                        } else if (dp.style == CanvasStyle.WATER_LINE) {
+                            mCanvas!!.drawPath(dp.path, mPaintWater)
+                            mCanvas!!.drawPath(dp.path, mPaintWater1)
+                        } else if (dp.style == CanvasStyle.PARKING_LINE) {
+                            mCanvas!!.drawPath(dp.path, mPaintParking)
+                            mCanvas!!.drawPath(dp.path, mPaintParking1)
+                        } else if (dp.style == CanvasStyle.CIRCULAR_POINT) {
+                            mPaintCircular.color = dp.color
+                            mCanvas!!.drawCircle(
+                                dp.pointList!![0].x.toFloat(),
+                                dp.pointList!![0].y.toFloat(),
+                                dp.width.toFloat(),
+                                mPaintCircular
+                            )
+                        } else if (dp.style == CanvasStyle.POLY_LINE) {
+                            mPaintCircular.color = dp.color
+                            dp.path.reset()
+                            if (dp.pointList != null && dp.pointList!!.size > 0) {
+                                for (k in dp.pointList!!.indices) {
+                                    if (k == 0) {
+                                        mCanvas!!.drawCircle(
+                                            dp.pointList!![k].x.toFloat(),
+                                            dp.pointList!![k].y.toFloat(),
+                                            2f,
+                                            mPaintCircular
+                                        )
+                                        dp.path.moveTo(
+                                            dp.pointList!![k].x.toFloat(),
+                                            dp.pointList!![k].y.toFloat()
+                                        )
+                                    } else {
+                                        dp.path.lineTo(
+                                            dp.pointList!![k].x.toFloat(),
+                                            dp.pointList!![k].y.toFloat()
+                                        )
+                                    }
+                                }
+                            }
+                            mCanvas!!.drawPath(dp.path, dp.getPaint(mPaint)!!)
+                        } else {
+                            mCanvas!!.drawPath(dp.path, dp.getPaint(mPaint)!!)
+                        }
+                    }
+                    invalidate() // 刷新
+                    return
+                }
+            }
+        }
+        invalidate()
+    }
+
+    /**
+     * 整条线段是否穿过矩形
+     *
+     * @param pointList
+     * @param rect
+     * @return
+     */
+    private fun LineIntersectRect(pointList: List<Point>?, rect: Rect?): Boolean {
+        if (pointList != null && pointList.size > 1 && rect != null && !rect.isEmpty) {
+            for (i in 0 until pointList.size - 1) {
+                if (CheckRectLine(pointList[i], pointList[i + 1], rect)) return true
+            }
+        } else if (pointList != null && pointList.size == 1) {
+            val point = pointList[0]
+            if (point.x >= rect!!.left && point.x <= rect.right && point.y >= rect.top && point.y <= rect.bottom) {
+                return true
+            }
+        }
+        return false
+    }
+
+    /**
+     * 获取画布上的图形数据
+     *
+     * @return
+     */
+    val paths: List<DrawPath>?
+        get() {
+            setPolyLineOver()
+            if (mCurrentPaths.size > 0) {
+                var i = 0
+                while (i < mCurrentPaths.size) {
+                    val drawPath = mCurrentPaths[i]
+                    if (drawPath.style == CanvasStyle.POLY_LINE
+                        || drawPath.style == CanvasStyle.RAILWAY_LINE
+                    ) {
+                        if (drawPath.pointList!!.size < 2) {
+                            mCurrentPaths.remove(drawPath)
+                            i--
+                        }
+                    } else if (drawPath.style == CanvasStyle.WATER_LINE || drawPath.style == CanvasStyle.GREENLAND_LINE || drawPath.style == CanvasStyle.PARKING_LINE) {
+                        if (drawPath.pointList!!.size < 4) {
+                            mCurrentPaths.remove(drawPath)
+                            i--
+                        }
+                    }
+                    i++
+                }
+            }
+            return mCurrentPaths
+        }
+
+    /**
+     * 得到缩略图
+     *
+     * @return
+     */
+    val thumbnail: Bitmap?
+        get() {
+            if (mCurrentPaths == null || mCurrentPaths!!.size == 0) {
+                return null
+            }
+            val whiteBgBitmap = Bitmap.createBitmap(
+                mBitmap!!.width,
+                mBitmap!!.height,
+                Bitmap.Config.ARGB_8888
+            )
+            val canvas = Canvas(whiteBgBitmap)
+            canvas.drawColor(Color.WHITE)
+            canvas.drawBitmap(mBitmap!!, 0f, 0f, null)
+            return ThumbnailUtils.extractThumbnail(whiteBgBitmap, 100, 100)
+        }
+
+    /**
+     * 设置是否保存中间点信息
+     *
+     * @param b
+     */
+    fun setIsSavePoint(b: Boolean) {
+        isSavePoint = b
+    }
+
+    /**
+     * 设置橡皮擦按钮是否开启或关闭
+     *
+     * @param b
+     */
+    fun setEraser(b: Boolean) {
+        isEraser = b
+        if (b) {
+            setPolyLineOver()
+        }
+    }
+
+    /**
+     * 判断线与垂直线相交
+     *
+     * @param startX
+     * @param startY
+     * @param endX
+     * @param endY
+     * @param x0
+     * @param y1
+     * @param y2
+     * @return
+     */
+    private fun CheckRectLineV(
+        startX: Float,
+        startY: Float,
+        endX: Float,
+        endY: Float,
+        x0: Float,
+        y1: Float,
+        y2: Float
+    ): Boolean {
+        if (x0 < startX && x0 < endX) return false
+        if (x0 > startX && x0 > endX) return false
+        if (startX == endX) {
+            return if (x0 == startX) {
+                if (startY < y1 && endY < y1) return false
+                !(startY > y2 && endY > y2)
+            } else {
+                false
+            }
+        }
+        val y = (endY - startY) * (x0 - startX) / (endX - startX) + startY
+        return y in y1..y2
+    }
+
+    /**
+     * 判断线与水平线相交
+     *
+     * @param startX
+     * @param startY
+     * @param endX
+     * @param endY
+     * @param y0
+     * @param x1
+     * @param x2
+     * @return
+     */
+    private fun CheckRectLineH(
+        startX: Float,
+        startY: Float,
+        endX: Float,
+        endY: Float,
+        y0: Float,
+        x1: Float,
+        x2: Float
+    ): Boolean {
+        //直线在点的上方
+        if (y0 < startY && y0 < endY) return false
+        //直线在点的下方
+        if (y0 > startY && y0 > endY) return false
+        //水平直线
+        if (startY == endY) {
+            //水平直线与点处于同一水平。
+            return if (y0 == startY) {
+                //直线在点的左边
+                if (startX < x1 && endX < x1) return false
+                //直线在x2垂直线右边
+                !(startX > x2 && endX > x2)
+                //直线的部分或者全部处于点与x2垂直线之间
+            } else  //水平直线与点不处于同一水平。
+            {
+                false
+            }
+        }
+        //斜线
+        val x = (endX - startX) * (y0 - startY) / (endY - startY) + startX
+        return x in x1..x2
+    }
+
+    /**
+     * 直线是否穿过矩形
+     *
+     * @param start
+     * @param end
+     * @param rect
+     * @return
+     */
+    private fun CheckRectLine(start: Point, end: Point, rect: Rect): Boolean {
+        var result = false
+        if (rect.contains(start.x, start.y) || rect.contains(end.x, end.y)) result = true else {
+            if (CheckRectLineH(
+                    start.x.toFloat(),
+                    start.y.toFloat(),
+                    end.x.toFloat(),
+                    end.y.toFloat(),
+                    rect.top.toFloat(),
+                    rect.left.toFloat(),
+                    rect.right.toFloat()
+                )
+            ) return true
+            if (CheckRectLineH(
+                    start.x.toFloat(),
+                    start.y.toFloat(),
+                    end.x.toFloat(),
+                    end.y.toFloat(),
+                    rect.bottom.toFloat(),
+                    rect.left.toFloat(),
+                    rect.right.toFloat()
+                )
+            ) return true
+            if (CheckRectLineV(
+                    start.x.toFloat(),
+                    start.y.toFloat(),
+                    end.x.toFloat(),
+                    end.y.toFloat(),
+                    rect.left.toFloat(),
+                    rect.top.toFloat(),
+                    rect.bottom.toFloat()
+                )
+            ) return true
+            if (CheckRectLineV(
+                    start.x.toFloat(),
+                    start.y.toFloat(),
+                    end.x.toFloat(),
+                    end.y.toFloat(),
+                    rect.right.toFloat(),
+                    rect.top.toFloat(),
+                    rect.bottom.toFloat()
+                )
+            ) return true
+        }
+        return result
+    }
+
+    /**
+     * 两个矩形是否包含
+     *
+     * @param x1
+     * @param y1
+     * @param x2
+     * @param y2
+     * @param dis
+     * @return
+     */
+    private fun contains(x1: Int, y1: Int, x2: Int, y2: Int, dis: Int): Boolean {
+        val rect = Rect(x1 - dis, y1 - dis, x1 + dis, y1 + dis)
+        return rect.contains(x2, y2)
+    }
+
+    companion object {
+        /*
+        橡皮擦捕捉半径
+     */
+        private const val EraserWight = 12
+
+        /**
+         * 线点 抽稀半径
+         */
+        private const val ChouXiBanJing = 12 //抽稀半径
+
+        /**
+         * 手指滑动的距离,两点之间像素值少于4 的点不要
+         */
+        private const val TOUCH_TOLERANCE = 4f
+    }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/navinfo/omqs/ui/fragment/note/CanvasViewHelper.kt b/app/src/main/java/com/navinfo/omqs/ui/fragment/note/CanvasViewHelper.kt
new file mode 100644
index 00000000..fc0e1805
--- /dev/null
+++ b/app/src/main/java/com/navinfo/omqs/ui/fragment/note/CanvasViewHelper.kt
@@ -0,0 +1,424 @@
+package com.navinfo.omqs.ui.fragment.note
+
+import android.graphics.Path
+import android.graphics.Point
+import android.graphics.Rect
+import android.graphics.RectF
+import android.text.TextUtils
+import com.navinfo.collect.library.data.entity.NoteBean
+import com.navinfo.collect.library.data.entity.SketchAttachContent
+import com.navinfo.collect.library.map.NIMapController
+import com.navinfo.collect.library.utils.GeometryTools
+import com.navinfo.omqs.ui.fragment.note.CanvasView.CanvasStyle
+import io.realm.RealmList
+import org.locationtech.jts.geom.Coordinate
+import org.oscim.backend.canvas.Color
+import org.oscim.core.GeoPoint
+import java.util.UUID
+import kotlin.math.abs
+import kotlin.math.cos
+import kotlin.math.sin
+
+/**
+ * @author zhjch
+ * @version V1.0
+ * @ClassName: CanvasViewHelper
+ * @Date 2016/5/16
+ * @Description: ${TODO}(用一句话描述该文件做什么)
+ */
+object CanvasViewHelper {
+    private const val mD2I = 3600000
+    fun createNoteBean(
+        controller: NIMapController,
+        mCurrentPaths: List<CanvasView.DrawPath>,
+    ): NoteBean {
+        val noteBean = NoteBean(UUID.randomUUID().toString())
+        if (mCurrentPaths.isNotEmpty()) {
+            val list: RealmList<SketchAttachContent> = RealmList<SketchAttachContent>()
+            noteBean.list = list
+            for (index in mCurrentPaths.indices) {
+                val dp: CanvasView.DrawPath = mCurrentPaths[index]
+                val geo = SketchAttachContent(UUID.randomUUID().toString())
+                val pointList = dp.pointList ?: continue
+                if (dp.style === CanvasStyle.GREENLAND_LINE || dp.style === CanvasStyle.WATER_LINE || dp.style === CanvasStyle.PARKING_LINE) {
+                    val geoPointList = mutableListOf<GeoPoint>()
+                    for (i in pointList.indices) {
+                        val point = pointList[i]
+                        val geoPoint: GeoPoint = controller.viewportHandler.fromScreenPoint(
+                            point
+                        )
+                        geoPointList.add(geoPoint)
+                        if (index == 0 && i == 0) {
+                            noteBean.guideGeometry =
+                                GeometryTools.createGeometry(geoPoint).toText()
+                        }
+                    }
+                    geo.style = createLineStyle(dp.style, dp.width, dp.color)
+                    geo.geometry = GeometryTools.createPolygon(geoPointList).toText()
+                } else if (dp.style === CanvasStyle.CIRCULAR_POINT) {
+                    val point = pointList[0]
+                    val geoPoint: GeoPoint = controller.viewportHandler.fromScreenPoint(point)
+                    geo.style = createLineStyle(dp.style, dp.width, dp.color)
+                    geo.geometry = GeometryTools.createGeometry(geoPoint).toText()
+                    noteBean.guideGeometry = geo.geometry
+                } else if (dp.style === CanvasStyle.ELLIPSE_LINE) {
+                    dp.rect?.let {
+                        val pointLT = Point(it.left, it.top)
+                        val pointRB = Point(it.right, it.bottom)
+                        val geoPointLT: GeoPoint =
+                            controller.viewportHandler.fromScreenPoint(pointLT)
+                        val geoPointRB: GeoPoint =
+                            controller.viewportHandler.fromScreenPoint(pointRB)
+                        val minX: Double
+                        val maxX: Double
+                        val minY: Double
+                        val maxY: Double
+                        if (geoPointLT.longitude < geoPointRB.longitude) {
+                            minX = (geoPointLT.longitude * mD2I)
+                            maxX = (geoPointRB.longitude * mD2I)
+                        } else {
+                            minX = (geoPointRB.longitude * mD2I)
+                            maxX = (geoPointLT.longitude * mD2I)
+                        }
+                        if (geoPointLT.latitude < geoPointRB.latitude) {
+                            minY = (geoPointLT.latitude * mD2I)
+                            maxY = (geoPointRB.latitude * mD2I)
+                        } else {
+                            minY = (geoPointRB.latitude * mD2I)
+                            maxY = (geoPointLT.latitude * mD2I)
+                        }
+                        val xR = (maxX - minX) / 2
+                        val yR = (maxY - minY) / 2
+                        var a = 0.0
+                        var tempX = xR * cos(a) + xR + minX
+                        val tempY = yR * sin(a) + yR + minY
+                        val firstX = tempX
+                        val geoPointList = mutableListOf<GeoPoint>()
+                        geoPointList.add(GeoPoint(tempX / mD2I, tempY / mD2I))
+                        var bLeft = false
+                        var bRight = false
+                        var zeng = 0.1
+                        if (controller.mMapView.mapLevel >= 20) {
+                            zeng = 0.2
+                        }
+                        while (!bLeft || !bRight) {
+                            a += zeng
+                            val x1 = (xR * cos(a) + xR + minX).toInt().toDouble()
+                            val y1 = (yR * sin(a) + yR + minY).toInt().toDouble()
+                            if (!bLeft && x1 > tempX) {
+                                bLeft = true
+                            }
+                            if (!bRight && bLeft && x1 <= tempX) {
+                                bRight = true
+                                geoPointList.add(
+                                    GeoPoint(
+                                        firstX / mD2I,
+                                        tempY / mD2I
+                                    )
+                                )
+                            } else {
+                                tempX = x1
+                                geoPointList.add(GeoPoint(x1 / mD2I, y1 / mD2I))
+                            }
+                        }
+                        if (index == 0) {
+                            noteBean.guideGeometry =
+                                GeometryTools.createGeometry(geoPointList[0]).toText()
+                        }
+                        geo.style = createLineStyle(dp.style, dp.width, dp.color)
+                        geo.geometry = GeometryTools.createLineString(geoPointList).toText()
+                    }
+                } else {
+                    val geoPointList = mutableListOf<GeoPoint>()
+                    for (i in pointList.indices) {
+                        val point = pointList[i]
+                        val geoPoint: GeoPoint =
+                            controller.viewportHandler.fromScreenPoint(point)
+                        geoPointList.add(geoPoint)
+                        if (index == 0 && i == 0) {
+                            noteBean.guideGeometry =
+                                GeometryTools.createGeometry(geoPoint).toText()
+                        }
+                    }
+                    geo.style = createLineStyle(dp.style, dp.width, dp.color)
+                    geo.geometry = GeometryTools.createLineString(geoPointList).toText()
+                }
+                list.add(geo)
+            }
+        }
+        return noteBean
+    }
+
+    fun createDrawPaths(
+        controller: NIMapController,
+        att: NoteBean
+    ): MutableList<CanvasView.DrawPath> {
+        val contents: List<SketchAttachContent> = att.list
+        val drawPaths: MutableList<CanvasView.DrawPath> = mutableListOf()
+        var width = 5
+        var canvasStyle = CanvasStyle.FREE_LINE
+        var color = Color.BLACK
+        for (geo in contents) {
+            var max_x = 0
+            var max_y = 0
+            var min_x = 0
+            var min_y = 0
+            val style = geo.style
+            if (!TextUtils.isEmpty(style) && style.length > 3) {
+                try {
+                    if (style.startsWith("4")) {
+                        canvasStyle = CanvasStyle.RAILWAY_LINE
+                    } else if (style.startsWith("5")) {
+                        if (style.contains("cde3ac")) {
+                            canvasStyle = CanvasStyle.GREENLAND_LINE
+                        } else if (style.contains("abcaff")) {
+                            canvasStyle = CanvasStyle.WATER_LINE
+                        } else if (style.contains("fffe98")) {
+                            canvasStyle = CanvasStyle.PARKING_LINE
+                        }
+                    } else {
+                        val s = style.substring(0, 1)
+                        if (TextUtils.equals(s, "2")) {
+                            canvasStyle = CanvasStyle.STRAIGHT_LINE
+                        } else if (TextUtils.equals(s, "3")) {
+                            canvasStyle = CanvasStyle.RECT_LINE
+                        } else if (TextUtils.equals(s, "6")) {
+                            canvasStyle = CanvasStyle.POLY_LINE
+                        } else if (TextUtils.equals(s, "7")) {
+                            canvasStyle = CanvasStyle.ELLIPSE_LINE
+                        } else if (TextUtils.equals(s, "9")) {
+                            canvasStyle = CanvasStyle.CIRCULAR_POINT
+                        } else if (TextUtils.equals(s, "1")) {
+                            canvasStyle = CanvasStyle.FREE_LINE
+                        }
+                        width = style.substring(1, 3).toInt()
+                        var colorStr = style.substring(3, style.length)
+                        if (colorStr.length == 6) {
+                            colorStr = "ff$colorStr"
+                        } else if (colorStr.length == 8) {
+                        } else {
+                            colorStr = "ff000000"
+                        }
+                        color = colorStr.toLong(16).toInt()
+                    }
+                } catch (e: Exception) {
+                    e.printStackTrace()
+                }
+                val path = Path()
+                val pointList: MutableList<Point> = ArrayList()
+                if (canvasStyle === CanvasStyle.GREENLAND_LINE || canvasStyle === CanvasStyle.WATER_LINE || canvasStyle === CanvasStyle.PARKING_LINE) {
+//                    val polygonGeometry: PolygonGeometry = geo.geo as PolygonGeometry
+//                    if (polygonGeometry != null) {
+//                        val xyz: Array<Array<DoubleArray>> = polygonGeometry.getCoordinates()
+//                        if (xyz != null && xyz.isNotEmpty() && xyz[0].size > 1) {
+//                            var geoPoint: GeoPoint? = GeoPoint(xyz[0][0][0], xyz[0][0][1])
+//                            val movePoint: Point = .geoToScreen(geoPoint)
+//                            max_x = movePoint.x
+//                            max_y = movePoint.y
+//                            min_x = movePoint.x
+//                            min_y = movePoint.y
+//                            path.reset()
+//                            path.moveTo(movePoint.x.toFloat(), movePoint.y.toFloat())
+//                            pointList.add(Point(movePoint.x, movePoint.y))
+//                            for (i in 1 until xyz[0].size) {
+//                                val x_y = xyz[0][i]
+//                                if (x_y != null) {
+//                                    geoPoint = GeoPoint(x_y[0], x_y[1])
+//                                    val point: Point = projection.geoToScreen(geoPoint)
+//                                    if (point.x > max_x) {
+//                                        max_x = point.x
+//                                    }
+//                                    if (point.x < min_x) {
+//                                        min_x = point.x
+//                                    }
+//                                    if (point.y > max_y) {
+//                                        max_y = point.y
+//                                    }
+//                                    if (point.y < min_y) {
+//                                        min_y = point.y
+//                                    }
+//                                    path.lineTo(point.x.toFloat(), point.y.toFloat())
+//                                    pointList.add(point)
+//                                }
+//                            }
+//                            path.close()
+//                        }
+//                    }
+//                    val drawPath =
+//                        CanvasView.DrawPath(pointList[0], path, width, color, canvasStyle)
+//                    val rect = Rect(min_x, min_y, max_x, max_y)
+//                    drawPath.rect = rect
+//                    drawPath.pointList = pointList
+//                    drawPaths.add(drawPath)
+                } else if (canvasStyle === CanvasStyle.CIRCULAR_POINT) {
+//                    val pointGeometry: PointGeometry = geo.geo as PointGeometry
+//                    if (pointGeometry != null && pointGeometry.getCoordinates() != null) {
+//                        val geoPoint: GeoPoint = GeoPoint(
+//                            pointGeometry.getCoordinates().get(0),
+//                            pointGeometry.getCoordinates().get(1)
+//                        )
+//                        val movePoint: Point = projection.geoToScreen(geoPoint)
+//                        pointList.add(movePoint)
+//                        val drawPath = DrawPath(movePoint, path, width, color, canvasStyle)
+//                        val rect = Rect(
+//                            movePoint.x - width - 20,
+//                            movePoint.y - width - 20,
+//                            movePoint.x + width + 20,
+//                            movePoint.y + width + 20
+//                        )
+//                        drawPath.rect = rect
+//                        drawPath.pointList = pointList
+//                        drawPaths.add(drawPath)
+//                    }
+                } else if (canvasStyle === CanvasStyle.ELLIPSE_LINE) {
+//                    val lineGeometry = GeometryTools.createGeometry(geo.geometry)
+//                    if (lineGeometry != null) {
+//                        val xys: Array<out Coordinate> = lineGeometry.coordinates
+//                        if (xys != null && xys.size > 1) {
+//                            var geoPoint: GeoPoint? = GeoPoint(xys[0].y, xys[0].x)
+//                            val movePoint: Point = projection.geoToScreen(geoPoint)
+//                            max_x = movePoint.x
+//                            max_y = movePoint.y
+//                            min_x = movePoint.x
+//                            min_y = movePoint.y
+//                            path.reset()
+//                            path.moveTo(movePoint.x.toFloat(), movePoint.y.toFloat())
+//                            pointList.add(Point(movePoint.x, movePoint.y))
+//                            for (i in 1 until xys.size) {
+//                                val x_y = xys[i]
+//                                geoPoint = GeoPoint(x_y[0], x_y[1])
+//                                val point: Point = projection.geoToScreen(geoPoint)
+//                                if (point.x > max_x) {
+//                                    max_x = point.x
+//                                }
+//                                if (point.x < min_x) {
+//                                    min_x = point.x
+//                                }
+//                                if (point.y > max_y) {
+//                                    max_y = point.y
+//                                }
+//                                if (point.y < min_y) {
+//                                    min_y = point.y
+//                                }
+//                                pointList.add(point)
+//                            }
+//                            path.addOval(
+//                                RectF(
+//                                    min_x.toFloat(),
+//                                    min_y.toFloat(),
+//                                    max_x.toFloat(),
+//                                    max_y.toFloat()
+//                                ), Path.Direction.CW
+//                            )
+//                        }
+//                    }
+//                    val drawPath =
+//                        CanvasView.DrawPath(pointList[0], path, width, color, canvasStyle)
+//                    val rect = Rect(min_x, min_y, max_x, max_y)
+//                    drawPath.rect = rect
+//                    drawPath.pointList = pointList
+//                    drawPaths.add(drawPath)
+                } else {
+                    val lineGeometry = GeometryTools.createGeometry(geo.geometry)
+                    if (lineGeometry != null) {
+                        val xys: Array<out Coordinate> = lineGeometry.coordinates
+                        if (xys.size > 1) {
+                            var geoPoint = GeoPoint(xys[0].y, xys[0].x)
+                            val movePoint: Point =
+                                controller.viewportHandler.toScreenPoint(geoPoint)
+                            max_x = movePoint.x
+                            max_y = movePoint.y
+                            min_x = movePoint.x
+                            min_y = movePoint.y
+                            path.reset()
+                            path.moveTo(movePoint.x.toFloat(), movePoint.y.toFloat())
+                            pointList.add(Point(movePoint.x, movePoint.y))
+                            for (i in 1 until xys.size) {
+                                val x_y = xys[i]
+                                geoPoint = GeoPoint(x_y.y, x_y.x)
+                                val point: Point =
+                                    controller.viewportHandler.toScreenPoint(geoPoint)
+                                if (point.x > max_x) {
+                                    max_x = point.x
+                                }
+                                if (point.x < min_x) {
+                                    min_x = point.x
+                                }
+                                if (point.y > max_y) {
+                                    max_y = point.y
+                                }
+                                if (point.y < min_y) {
+                                    min_y = point.y
+                                }
+                                if (canvasStyle === CanvasStyle.FREE_LINE) {
+                                    val dx = abs(point.x - movePoint.x).toFloat()
+                                    val dy = abs(point.y - movePoint.y).toFloat()
+                                    if (dx >= 4 || dy >= 4) {
+                                        path.quadTo(
+                                            movePoint.x.toFloat(),
+                                            movePoint.y.toFloat(),
+                                            ((point.x + movePoint.x) / 2).toFloat(),
+                                            ((point.y + movePoint.y) / 2).toFloat()
+                                        ) //源代码是这样写的,可是我没有弄明白,为什么要这样?
+                                        movePoint.x = point.x
+                                        movePoint.y = point.y
+                                    }
+                                } else {
+                                    path.lineTo(point.x.toFloat(), point.y.toFloat())
+                                }
+                                pointList.add(point)
+                            }
+                        }
+                    }
+                    val drawPath =
+                        CanvasView.DrawPath(pointList[0], path, width, color, canvasStyle)
+                    val rect = Rect(min_x, min_y, max_x, max_y)
+                    drawPath.rect = rect
+                    drawPath.pointList = pointList
+                    drawPaths.add(drawPath)
+                }
+            }
+        }
+        return drawPaths
+    }
+
+    private fun createLineStyle(canvasStyle: CanvasStyle, width: Int, color: Int): String {
+        val style = StringBuilder()
+        if (canvasStyle === CanvasStyle.RAILWAY_LINE) {
+            return "4060070c004ffffff16"
+        } else if (canvasStyle === CanvasStyle.GREENLAND_LINE) {
+            return "50200b050cde3ac"
+        } else if (canvasStyle === CanvasStyle.WATER_LINE) {
+            return "50200b050abcaff"
+        } else if (canvasStyle === CanvasStyle.PARKING_LINE) {
+            return "502a6a6a6fffe98"
+        }
+        if (canvasStyle === CanvasStyle.STRAIGHT_LINE) {
+            style.append("2")
+        } else if (canvasStyle === CanvasStyle.RECT_LINE) {
+            style.append("3")
+        } else if (canvasStyle === CanvasStyle.POLY_LINE) {
+            style.append("6")
+        } else if (canvasStyle === CanvasStyle.ELLIPSE_LINE) {
+            style.append("7")
+        } else if (canvasStyle === CanvasStyle.CIRCULAR_POINT) {
+            style.append("9")
+        } else {
+            style.append("1")
+        }
+        if (width < 10) {
+            style.append("0")
+        }
+        style.append(width.toString())
+        try {
+            var colorString = Integer.toHexString(color).toString()
+            if (colorString.length == 8) {
+                colorString = TextUtils.substring(colorString, 2, 8)
+            }
+            style.append(colorString)
+        } catch (e: Exception) {
+            e.printStackTrace()
+        }
+        return style.toString()
+    }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/navinfo/omqs/ui/fragment/note/NoteFragment.kt b/app/src/main/java/com/navinfo/omqs/ui/fragment/note/NoteFragment.kt
new file mode 100644
index 00000000..9d4cd8c2
--- /dev/null
+++ b/app/src/main/java/com/navinfo/omqs/ui/fragment/note/NoteFragment.kt
@@ -0,0 +1,135 @@
+package com.navinfo.omqs.ui.fragment.note
+
+import android.os.Bundle
+import android.text.Editable
+import android.text.TextWatcher
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.navigation.findNavController
+import androidx.navigation.fragment.findNavController
+import com.navinfo.omqs.R
+import com.navinfo.omqs.databinding.FragmentNoteBinding
+import com.navinfo.omqs.ui.dialog.FirstDialog
+import com.navinfo.omqs.ui.fragment.BaseFragment
+import com.navinfo.omqs.ui.other.shareViewModels
+import dagger.hilt.android.AndroidEntryPoint
+
+@AndroidEntryPoint
+class NoteFragment : BaseFragment(), View.OnClickListener {
+    private var _binding: FragmentNoteBinding? = null
+    private val binding get() = _binding!!
+
+    private val viewModel by shareViewModels<NoteViewModel>("note")
+
+    override fun onCreateView(
+        inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
+    ): View {
+        _binding = FragmentNoteBinding.inflate(inflater, container, false)
+        return binding.root
+    }
+
+    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+        binding.sketchEraser.setOnClickListener(this)
+        binding.sketchClear.setOnClickListener(this)
+        binding.sketchForward.setOnClickListener(this)
+        binding.sketchBack.setOnClickListener(this)
+        binding.noteBarSave.setOnClickListener(this)
+        binding.noteBarCancel.setOnClickListener(this)
+        binding.noteBarDelete.setOnClickListener(this)
+        binding.noteDescription.addTextChangedListener(object : TextWatcher {
+            override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
+            }
+
+            override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
+            }
+
+            override fun afterTextChanged(s: Editable?) {
+                viewModel.noteBeanDescription = s.toString()
+            }
+        })
+        /**
+         * 数据操作结束
+         */
+        viewModel.liveDataFinish.observe(viewLifecycleOwner) {
+            if (it)
+                onBackPressed()
+        }
+
+        /**
+         * 画布初始化完成
+         */
+
+        viewModel.liveDataCanvasViewInitFinished.observe(viewLifecycleOwner) {
+            if (it)
+                arguments?.let { b ->
+                    val id = b.getString("NoteId", "")
+                    if (id.isNotEmpty()) {
+                        viewModel.initData(id)
+                    }
+                }
+        }
+    }
+
+
+    override fun onStart() {
+        super.onStart()
+        activity?.run {
+            findNavController(
+                R.id.main_activity_middle_fragment
+            ).navigate(R.id.CanvasFragment)
+        }
+    }
+
+    override fun onStop() {
+        super.onStop()
+        activity?.run {
+            findNavController(
+                R.id.main_activity_middle_fragment
+            ).navigateUp()
+        }
+    }
+
+    override fun onClick(v: View) {
+        when (v) {
+            binding.sketchEraser -> {
+                viewModel.onEraser()
+                binding.sketchEraser.isSelected = viewModel.isEraser
+            }
+            binding.sketchBack -> {
+                viewModel.onBack()
+            }
+            binding.sketchForward -> {
+                viewModel.onForward()
+            }
+            binding.sketchClear -> {
+                viewModel.onClear()
+            }
+            binding.noteBarSave -> {
+                viewModel.onSaveData()
+            }
+            binding.noteBarDelete -> {
+                viewModel.deleteData(requireContext())
+            }
+            binding.noteBarCancel -> {
+                //返回按钮点击
+                val mDialog = FirstDialog(context)
+                mDialog.setTitle("提示?")
+                mDialog.setMessage("是否退出,请确认!")
+                mDialog.setPositiveButton(
+                    "确定"
+                ) { _, _ ->
+                    mDialog.dismiss()
+                    onBackPressed()
+                }
+                mDialog.setNegativeButton("取消", null)
+                mDialog.show()
+            }
+        }
+    }
+
+    override fun onBackPressed(): Boolean {
+        findNavController().navigateUp()
+        return true
+    }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/navinfo/omqs/ui/fragment/note/NoteViewModel.kt b/app/src/main/java/com/navinfo/omqs/ui/fragment/note/NoteViewModel.kt
new file mode 100644
index 00000000..d5fd927c
--- /dev/null
+++ b/app/src/main/java/com/navinfo/omqs/ui/fragment/note/NoteViewModel.kt
@@ -0,0 +1,171 @@
+package com.navinfo.omqs.ui.fragment.note
+
+import android.content.Context
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import com.navinfo.collect.library.data.entity.NoteBean
+import com.navinfo.collect.library.map.NIMapController
+import com.navinfo.omqs.ui.dialog.FirstDialog
+import dagger.hilt.android.lifecycle.HiltViewModel
+import io.realm.Realm
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import javax.inject.Inject
+
+@HiltViewModel
+class NoteViewModel @Inject constructor(
+    val mapController: NIMapController
+) : ViewModel() {
+
+    lateinit var canvasView: CanvasView
+
+
+    var mNoteBean: NoteBean? = null
+
+
+    var isEraser = false
+
+    var noteBeanDescription = ""
+//    /**
+//     * 橡皮擦开关
+//     */
+//    val liveEraserData = MutableLiveData(false)
+//
+//    /**
+//     * 清除按钮
+//     */
+//    val liveClearData = MutableLiveData(false)
+//
+//    /**
+//     * 回退按钮
+//     */
+//    val liveBackData = MutableLiveData(false)
+//
+//    /**
+//     * 撤销按钮
+//     */
+//    val liveForward = MutableLiveData(false)
+
+    /**
+     * 处理结束关闭fragment
+     */
+    val liveDataFinish = MutableLiveData(false)
+
+    /**
+     * 通知页面画布初始化完成
+     */
+    val liveDataCanvasViewInitFinished = MutableLiveData(false)
+
+    fun initCanvasView(canvasView: CanvasView) {
+        this.canvasView = canvasView
+        liveDataCanvasViewInitFinished.value = true
+    }
+
+
+    /**
+     * 通知橡皮擦开关
+     */
+    fun onEraser() {
+        isEraser = !isEraser
+        canvasView.setEraser(isEraser)
+//        liveEraserData.value = !liveEraserData.value!!
+    }
+
+    /**
+     * 通知清除
+     */
+    fun onClear() {
+        canvasView.removeAllPaint()
+//        liveClearData.value = true
+    }
+
+    /**
+     * 通知回退
+     */
+    fun onBack() {
+        canvasView.back()
+//        liveBackData.value = true
+    }
+
+    /**
+     * 通知撤销回退
+     */
+    fun onForward() {
+        canvasView.forward()
+//        liveForward.value = true
+    }
+
+    /**
+     * 保存数据
+     */
+    fun onSaveData() {
+        viewModelScope.launch(Dispatchers.IO) {
+            if (canvasView.paths != null && canvasView.paths!!.isNotEmpty()) {
+                var noteBean =
+                    CanvasViewHelper.createNoteBean(mapController, canvasView.paths!!)
+                if (mNoteBean != null) {
+                    noteBean.id = mNoteBean!!.id
+                    noteBean.description = noteBeanDescription
+                }
+                mNoteBean = noteBean
+                val realm = Realm.getDefaultInstance()
+                realm.executeTransaction {
+                    it.copyToRealmOrUpdate(noteBean)
+                }
+                mapController.markerHandle.addOrUpdateNoteMark(mNoteBean!!)
+                liveDataFinish.postValue(true)
+            }
+        }
+    }
+
+    /**
+     * 删除数据
+     */
+    fun deleteData(context: Context) {
+        if (mNoteBean == null) {
+            liveDataFinish.postValue(true)
+            return
+        } else {
+            val mDialog = FirstDialog(context)
+            mDialog.setTitle("提示?")
+            mDialog.setMessage("是否删除标签,请确认!")
+            mDialog.setPositiveButton(
+                "确定"
+            ) { dialog, _ ->
+                dialog.dismiss()
+                viewModelScope.launch(Dispatchers.IO) {
+                    val realm = Realm.getDefaultInstance()
+                    realm.executeTransaction {
+                        val objects = it.where(NoteBean::class.java)
+                            .equalTo("id", mNoteBean!!.id).findFirst()
+                        objects?.deleteFromRealm()
+                    }
+                    mapController.markerHandle.removeNoteMark(mNoteBean!!)
+                    liveDataFinish.postValue(true)
+                }
+            }
+            mDialog.setNegativeButton("取消", null)
+            mDialog.show()
+        }
+    }
+
+    /**
+     * 初始化数据
+     */
+    fun initData(id: String) {
+        viewModelScope.launch(Dispatchers.IO) {
+            val realm = Realm.getDefaultInstance()
+            realm.executeTransaction { it ->
+                val objects = it.where(NoteBean::class.java)
+                    .equalTo("id", id).findFirst()
+                mNoteBean = realm.copyFromRealm(objects)
+                mNoteBean?.let { bean ->
+                    noteBeanDescription = bean.description
+                    val list = CanvasViewHelper.createDrawPaths(mapController, bean)
+                    canvasView.setDrawPathList(list)
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/app/src/main/res/drawable-xxhdpi/home_map_center.png b/app/src/main/res/drawable-xxhdpi/home_map_center.png
new file mode 100644
index 0000000000000000000000000000000000000000..f255a8a3a8b0d577da06a5c9ef313b5049ae0e60
GIT binary patch
literal 2150
zcmaJ?eK?f)8XiPZR)t!Trcoj0<HcZx8I2ia7nhx8myMV)^J13yF!MSyLsYDdB_D?~
zRKhtOlC4F$%H<;sL!{V)d@Lc>M{#11e6BOvwC9iGyw~-<zmMy_@8^D=-*f%mByTS_
zZLLFEFc?hR{WOKD9G#T!sa+b%?_G=1R^?y-x%fh7c)^f}Apl_{7B2{dyK@*JAQfb=
zBoQs3BMhc?luh%6d_6tzOdbct*u<d39KMnbgE=~h`3z<l2*HEE5H^>9n5nKqz}YMU
z!UyY#@#K@iQ1<C40eB|Li^hx!W8zo{CnDTYj8_V9K!^bsbHceoyqJLapo>?|H@DFU
z_y-6SMnL>w%Gc8yPUZ<fI2MIPGBJP++zy8Vuz-y{U<tRv0M=-XH5#x+0#<ko4i8}A
zA0LD=nt&CIr&3%$#!{{bh)@XP<I!l5NQ4quqj-W4G=Rh5&=@PUl@(HnKnf*Xh#^LD
zg(h1H6i~<%u=x<1$AxbyGJ<%K5CNfd`bP>J{wG<k@MD>j1w)G&d^CW<Y^JmY^z{7y
zP!8u4S_o0WKmGnsVIfVz2hmhe$cq#(l?NAWvKfkxCksFZ#1qhXyzs3mdWZ5Lo-mZh
zhm*;0GaojW#S;n5Kfpab@$Ot9#NaYPcM1Wa6hN`rEIbx#ZSP8Uwzsh&Sp$HJt+Oo!
zLw2#VCffqeSPIE`i%a1#BRL=!+TybQ<l?sFZgv8PuZ&Cq1?<0oELQ=K1OJdQp1r*n
z+imqea#`DZvD=o5R+>R?p6h>|^VXKKc{Zn?x>mXP)b}7)+3W&kR}ThHo5El_`rRoc
zn)ui0>pr10lHLy?y1OaBq1~Yr?YJMcuc${iR1wXB_I_zeS8JR@bQkQBn(D_;lWPA#
z3Z!Mp1H7kw<I8XQBEBhGJ+c^BX}M3d*!I#v7Cp>-`$M&`txgy+{%pYA?Ed(}xt2E<
zONMpDV{S0>r%rzA#$~jP>*68g-Xn=R@|`1Uic8{|JBVWW)E8^H(C*_$8-7xaiX#>r
z4%K=ijTqX^*YVeI89uh;AJ8aL%Y2L}Dz7@h=Bf0;+OiHR2#=}KuU1u1RQX~45ldAy
z*}lP{9X4t&Np9XU#d;_Gs6A-)i<ZVzNsD7l%3ASSzj~SGw8{+gEjlu5;&fMsY!^la
z8qpD`hVQJ9`CZp=)O>rWxo~!Vdh~{D#p?Tw*1`Gc8{f7|x-H%X-g2^#e|ajQfVlKH
zc2M8QZlS?ABqr1M!in{<6=dv*-ul~bG(vT3RXf#Mc-{SZ$iR%)kfr0XHml-9<k7UC
z@+Y0az`9<?%y&&0G{xalM74Ar?wK3)3G02)u}kN2(-wRv8_OxC=4WoC$bCo$Dz&SQ
zVG6F6L2Y}V%S?W8wM(U(jWtNPpn@Z6rnT+DrQF;%I=4{Xu5m_On~t2hm9(K=lUKjc
zXRY{aQeoV6`rYHn6_L4YszJW#h+iD3Cg54t3yn;B>Fn8#v2Owr^Pm3f9m<~G*Rcj0
z)xuR30t#~@fBA#BKxxuOW2N4x2Vfphl!31qWWH{C#+B|5@yi@pEj!;Vm%9p^y8EG4
zi_R&w&NR)V##L^|l(Y&IqM#8hf|xm`OC}XX#6D`VdhT7D_HB2v$aKQj=>3Zz+(D1E
zR>HbXT-G%In#qN9y~xz2_i5`UZNDle_bXzsxetIY=RP3cFD$mMRP&sm^QnFAbJf4e
zi29OSZ@U-cRZ`n`V(T^@<`~Am8n)<Jah2Zh$Uby2B9&Et_X;h8fbYxi2aDUv2VP~A
z-)1->OHGKa!_76V1(U^K<DT=buB+9Wi_ckpQ#s-*iFM8QZryu77OXX@Tg{@L-`9E#
zOzC>yYdflIVPpPCp7C<*>`7uzMuId)uhi(~nwh?2{=3XV099yh_&RY&;}MX2E&S?I
zOUjhts0}J$Fn?k`%D9-3H2a25EVTo?Mn@W1rPFG{i7{znpiF%1=f<ke_|)1Rl2kWN
zaeMt``zpHeXmbC43Dd++?)EiH<Q8BAO?1$D28<o0=g#8pr?=g%bXs~1wp!rRv*X(*
zm%JS%kiq!)a0Ge9GsNg-WP<5Q;>yzHzPY)8-z|H_TFRsgKKZ{lRHaq>DSk{_q0go|
z5cYegKB*T@nr6oCH0>SpPxjgK&x7L6N-ovXE0}JLSucWWqhC+p*7G8(Kj(+d<#{Z&
z=*(B#Sx&|wgDdYi#J|(42q-sM5H-lZX#T!-+{A1(nwKMP?wY5$czGvasUs(AE;!g7
zpYt6u>^@TD)HB|Qb8K1rt+IC^$8y%8vOeK*4gK?`16lE(sShNE%3fTZw=Y@vcjSQd
zB-Wrf+Z6NDSHog`+?A^(xEpz|q+PLv7k+CLDavCW8v1?RHfT%D(9Jdo_rzr^pLS^*
z4EXyjwKQI9d8rh1sND1NwtsH%J;q~;>;qeu$kM5rhtf$Iley@2Q3V#-q}6S(Y}vc{
O=XZDUqWtVkkNXcrmz(AQ

literal 0
HcmV?d00001

diff --git a/app/src/main/res/drawable-xxhdpi/sketch_back.png b/app/src/main/res/drawable-xxhdpi/sketch_back.png
new file mode 100644
index 0000000000000000000000000000000000000000..b42d27ec013b304ba3cd620cd4a606bb6d9a39e5
GIT binary patch
literal 1577
zcmaJ>dr;GM9FKHZ5Il5<Xhn(X5Zpo15Nc_qus)$`5o!f)K4u>Qy3!;jffnZD5IN>K
zJ)cvw=c>%<bW?}Q(@oGRK5)L+?(D{aPAj`p(QS(7m@Y-Y{V|ri<oCGG=ktAiF6*)~
z)5nexi~#^(tS&=s;MQ2~jSm{leSOxw&$wkWtI1;*kom0BMB{+MN}6#{=P(uE2Ha#V
zU49Wy0RY}~n=y~g)6bJxNC#~4V_=tq;@AL?lIEgJ7CX*@X1u^g$f4fm(-3I0%As7b
zUZkf~c%dz$jK&v~Wg0DIc8k;srKN%?E*U4_z*!UMaugAa%q52gbY<M$e~m!k0ED&6
zq1Q>}>9ar;N#mdx7AIIlXc8!q!l)QcN=D<sL=lQ1A`C&X1T;}5lFCpqIQT$ZG}@Xk
zGpMzLvAB~QDr8woh9FL-6Lw-SNf#ifR4PS8iAZ8%0*6RoN(t8FN+1~Fkb)X#EVPYc
zZ6pEu6-{Qcn3Y3ZrmstIP{XnWGuS4sVTj8_At)^Jmox;_>;E6>a15gv)_}js_dkUh
zV=09r2Am;_X$v>Fe4#%SB~#J3i6v<xNfr%tF{_YdNv4pbK$QxN%e4_!(#gymfa~=#
z9l@|B!h-A6a)=XvZ8ob+g(<Weky?r-D>W#pnWa!-T2+!lED>X>SrV0Uh^r<o#SWZc
zhq%@^T*Zi7zY`o37g>$dwi4W`rAY@kP_fK5axRh)^#-}tk#k8Nk&AH2ApW`jYtBPg
zoO%4)Vb^kpVc+8fXLg!%^+o)_EC2}Dqf;x4E^m9aKEs$jp{@5|H9kYwv|K6d-1*ZC
zMXU10cX8qD{<^En_wO!o2!l3*S=q<DnqYIbV03x(xR_P(Z$*rs@Pwy1yXpJ(7tzUQ
zid}!Ki0-@6y!!c*N2f}e`)Pf9oR8#WKsAqlW<OjmeY_|x$9?%~SD+Bgc7YO~p!K6o
zP|B8YX4JLNJr%Cod*3cvI{#*$pyT677dAFx9{w-m&@BrYTX%SzA&Ga%42OsAnk0<*
z3#A*E0q)4u3q^O~is|dm6BUQSDef#B9*0+YW<KC4_$yzIy4gLuiud&RkxNk<TGFYU
z>p*|tSx?BRz~;;YPh~y1h4ZL=@V)+qplRg`O0IO?9zXY)=sebfi7s~)TwEN}bMnRd
z2|ZD5=H0i(s(puAW*;q?*cMkbvCB$?wg<gpx9&Kn7BV58wN<x}?N2(3CNZa5Rz5zH
zUJ(X4XKPF0_8AXhUz$|?h4eRq&a7Fb?1t`NW4ET}D5mvA6jwH!3&wujhkCUu1Njqc
zd3sZLL*%_(vEAQPOAV<9c0`5vYNf5-@qc@AQx4VExnfV)>mJW9H!m$abuE$#{3P8S
zM)yZIfA%wQ&(kTauzLdHHST)r#uI$Od-D1X?2XD+Jd6KQcYn^qoDZf(wcAJc#xzDL
zypoc+N00X$saqs4j9L<;mG8Xs>4hZ?Tbs7u`Bl(kEP6C$=eDI)?wtN@i=)?g&#wJ4
zBl@nP<Kgiw68H7Ui0Ubuy$2R{>^?juoyh#Kw=MYF6VOQ~i`5HH))!;WcNVi)Y!@%2
zsXAni{Y4YuOMISeaym9t);>=@SkoA?qW`NJ>H7{}YuMB)*H;UWu)dOD!Q>i#>YvAI
uy*-MJUGD<(8$a*(Au8h8*T1iO6%Y#K1FoBpkSh28ymgvP^%3Q=HU9wFy-?}^

literal 0
HcmV?d00001

diff --git a/app/src/main/res/drawable-xxhdpi/sketch_eraser.png b/app/src/main/res/drawable-xxhdpi/sketch_eraser.png
new file mode 100644
index 0000000000000000000000000000000000000000..cb57b212d69fbf5c16dcfbc2dadef5c862bca201
GIT binary patch
literal 1599
zcmeAS@N?(olHy`uVBq!ia0vp^zCf(c!3HFknYup&Qj#UE5hcO-X(i=}MX3yqDfvmM
z3ZA)%>8U}fi7AzZCsS=07?>3^Ln2Bde0{8v^K<nQL2C3WatnaE85nHrD+&^mvr|hH
zl2X$%^K6yg@7}MZkeOnu6mIHk;9KCFnvv;IRg@ZB<rk7%m7Q#vqGWHUU{hfQG$1#%
zBvGLvHz%*ys=`(YY_1iM4HDK@QUEI{$+lI3I4Z(7K*2e`C{@8!&r~<rz|cg&+)~fb
z)X>DjP)EVYz|dIVz*yhVSl7_V%D~df&{P2mlz_GsrKDK}xwt{?0`hE?GD=Dctn~HE
z%ggo3jrH=2()A53EiLs8jP#9+bb%^#i!1X=5-W7`ij`p|xTF>*7iAWdWaj57fJ{tG
z$}cUkRRX#c;)UD-UthSvdBxCx0tT6WMPh-zp`L+$ZZ61QVAaJXL8%C5U{zflQdy9y
zACy|0Us{w5jE^*Bm}X$u;kO8+5a=2!m;B_?+|;}hPZwJypom^dW{Q=Yi>sxZk*m3(
zg_E11p{tpbrHP@dsj;cCk+FfRv9TpguV-FyNn&1dDok%CLa!NKy;jaesfi`|MIrh5
zIk4CZ$jC3rFV4s>P;hou&<M}WOUW-U)&zwt)S;<GnPsUdZbkXI3gDoz%EV&5rIVw9
zrJI=n(0W&}(@iYQoh%H@oDGZ&UCd3Lm7sc4uy`A)*AS;(eV}9XLGg+d&oCii;sG(?
zsTRnAr~A}AU}7%<rs|uwo9q}En6`MjIEGZ*dNVuPBP39y?fVgtKq0PQ3627>0-Y)b
z6AT18^;iNM7hdxInii50$DygE!n8YhAIqg+I~A>83sxvBap8}eA->DT!|kcFxyqD)
zS^tj4HSra@^}RbMb3V?##$b-+=4T%^U%#2X)>yd9Mak=lpY{<ZLth`^%STLhi~hSG
zzhPna#oeC+FL2iF{`=thUPbNjD6<TP_YQ&_o>j-hB}=BS&$X@KiffQDJR8}nVE!tC
zPdc3a($OtHqnPuKMK-D*tPWre>&}dtn(&zExk_V}!HL<eSC>tUOw~JgpGnHm{6^yH
zOw&2@@}{!?I;cD$YjS3_!$G0fXN5j6=4?M`va9WPTh_)vgQVun<p=zj3s-!#;Qz;V
zcT4GmwuKSr9r}5{Z_!pb>oCVNAn)+IouYG$MP8dpY(8l6N<Unx!&q#2H^-FB-v<?)
zH|iBuvE4F>iv7-W^cCw@vn_!kEol-R&P$K7>X|a-O1+&D&U>_q=~qt1jrGkb%!O)G
z<oH)Em_3cDPrA!>@xnrBpWi3XmfRJJmd!dPud*xho0Gf6+`7igUiKE1`%Xld6#o`3
zxg6GLJtruC%Dip+!m9Yf-fWumo<X;Bo<T+8ExQe!@$UZ7uXq00!Lv-P!<gA?D*uzK
zUfp4G)45G@V^6O(X!b2xwL9zJ2Hj<WUbQ`iHSe}e?Yg2rXWj0Dt5+27Z_YlY{^^j(
zuSmw;&C#;={GV0dW3XU|Yl_{ISo5@4qqhD=iquPC3m!hL=Y=I|8OdhFnVWb+8jhcG
z|FlavgFW(r&UE>WE~|4n?mQA%_t5u1)aA7DgY91YirKO}3(r1YbcsKvX1NQW;{HbC
zJ)d?Z@3Bwau#>+hbMvC=7&{BLd*=+lv|Q?cQnP$R|GK4|Cfc@ki|k6HeugOilt1{%
o#~+kjIx~}I&Dgf*i+=+ngK$9Afi!EW|DYn>)78&qol`;+01+@!IsgCw

literal 0
HcmV?d00001

diff --git a/app/src/main/res/drawable-xxhdpi/sketch_forward.png b/app/src/main/res/drawable-xxhdpi/sketch_forward.png
new file mode 100644
index 0000000000000000000000000000000000000000..c48f4029d5e91694904a127f6d9f39a98577a510
GIT binary patch
literal 1548
zcmaJ>drT8|9KV5}f;d({(P21?B8YA8N=vzu%K97y%cImt#30x9fKJ*wd&gQBZWQ7i
zx{-|-U{kO|5X?mE$b?BY@BxD80#5h97mSG}M&|${3fXo=!2L0H$=&af&*$@feR8GA
ziEsOOt@Z){z(*A)*Dz}+^KM$=$$Xa{zudztt7t_Uol0cUR?LC}5(A;bL6r&1#x*!*
z$lue8#{hu)TBA0NPE&71^@NFwxiDOtiDcLS5EE-7G5v0w26cG0(JW^F(cZxZjRrA$
zyHE|ONg1AFj4QC<sRfBzeZg)$VqnK^0b^_^BVfX5478bY%@k@AvuAWsX79S@vB4P#
zy<5zFnN*rO8I%zg929be96baJKoP=)g|HwR-URX?IFbiN^594g%ts*vg@xek!)Bsc
z3|Xi~uAGg<oW$%LnkG>m&uX=Dt&v>9lFfq=1mQt^9-q%)5F9GsOk*~VnF^UxkmHoz
zVkBuJVFq1_n2yM!#cU?imnE3Ud08_x+a{)AJR3&xU@qh;X%47X|3B1ZnnzQ#27i_B
ze+pCDd=lqraEi#Y=$XM~g}6eIsLX<6G-1&aMDAP{lXD1~pmGQjl*z!b?MAbKuu|bO
zaJ3p$nJF4G>v5G_%w`0*Mxz0hi6BG>BO*9js(@idloXOEBnUqeik1kGD4BGQD<|}M
zCfrQVaSgAy(F<~2PB4*7WI1jzzKa`_7QzJ1RE!!I&PBMO-YnO!a4t~`a(PTLJl9<R
zHRri2#yqa=yla`myzg-{V|EMU>SH#11OVLZD!D{!8@y7L5T{KEywp5WHCUo?!&!Zh
zw;Wzi*LwuSanl!_ZS-Z04Lpz*7I}-5U|oOcxttVE!CPQy@o(``-?d(_=$j|@0o}&b
z-ig15t;75us;8=`@9qY_f8}9KWCudkh{6Sb?HUK~cd{l&&J-7os=4-X-||ppxO=d~
zDS+dbx84dc>JIr0xwnO@KQ8ymu(w~k7}lG)L=f+HY@J=w&sA?t3V!g3RgyehxyG|W
zvLdW?&t6fq+hAu|ypv4^Xp327w_*)OE)gjDkbpjqr;Zgzwq3vW?6szY93|4O^KdM_
zHP*N*%DFSGvW1m!KXI_OEw=l>9UT_ols@r165BcOqUhd^s}~znJ3KG#t%`5#`l&cn
z6B4syx+f^G>~YV^w)*MvFMw^CU11e&8Iu#wQZkC)@hyEkeRZf-F|2@mM$f(e<Z!F-
zbMWwnK~~K`_3v*qADtR?9&_~7^`o}Sg=fA!=m`Fm7#TFJwr6(cs?NIeb^bvf&pNG(
zJ8Hhqz0sZbrlYK3-H*4s9t9p1FA^@Vp9pH1oZiru6<XgKSZ#>c=O&=62R(mQG&K)-
z#&$OiS+2jAy5~iD?U!dGPS(**{)LAhIP5F6H}XACjo;1rs(0L{`RKUT!54VYW5CU?
z)5ChQWA`4SBPtG@?oV&<D2dsha;F5}x4$f@uwo~<x!~I5^N-F~tR9(K>1~dHxT+Sf
zt`&YwU}oF43ALX;YpO5gex<+}aQej3grc9%3xbpG9oOxee6BL;s1Glk40EokdFowP
z`q}lm(XosZmAV%a$IjYcHh<di=ytjPdSyfPhQi~1>y`P-5>@^;=@w#aW4qfjK=g>*
TlGKI;x&F^8MWXzaG_&|0h*n9T

literal 0
HcmV?d00001

diff --git a/app/src/main/res/drawable/selector_sketch_eraser_bg.xml b/app/src/main/res/drawable/selector_sketch_eraser_bg.xml
new file mode 100644
index 00000000..d891b1c8
--- /dev/null
+++ b/app/src/main/res/drawable/selector_sketch_eraser_bg.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_selected="true">
+        <shape>
+            <padding android:bottom="2dp" android:left="@dimen/default_widget_padding" android:right="@dimen/default_widget_padding" android:top="2dp"></padding>
+            <stroke android:width="1.1dp" android:color="#1ABBFE" />
+        </shape>
+    </item>
+    <item>
+        <shape>
+            <padding android:bottom="@dimen/default_widget_padding" android:left="@dimen/default_widget_padding" android:right="@dimen/default_widget_padding" android:top="@dimen/default_widget_padding"></padding>
+            <solid android:color="@color/transp" />
+        </shape>
+    </item>
+
+</selector>
\ No newline at end of file
diff --git a/app/src/main/res/drawable/shape_vertical_dashed_line.xml b/app/src/main/res/drawable/shape_vertical_dashed_line.xml
index 5aa1a4bd..b9ca9aa6 100644
--- a/app/src/main/res/drawable/shape_vertical_dashed_line.xml
+++ b/app/src/main/res/drawable/shape_vertical_dashed_line.xml
@@ -4,7 +4,7 @@
         android:left="-300dp"
         android:right="-300dp">
         <rotate
-            android:drawable="@drawable/laneinfo_1_2"
+            android:drawable="@drawable/shape_dashed_line"
             android:fromDegrees="90" />
     </item>
 </layer-list>
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index eb4204c8..6a3dcc15 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -79,7 +79,7 @@
             android:layout_width="0dp"
             android:layout_height="wrap_content"
             android:layout_marginRight="@dimen/top_right_drawer_btns_mr"
-            app:constraint_referenced_ids="main_activity_serach,main_activity_2d_3d,main_activity_camera,main_activity_trace,main_activity_calc_disance,main_activity_menu"
+            app:constraint_referenced_ids="main_activity_serach,main_activity_2d_3d,main_activity_camera,main_activity_trace,main_activity_calc_disance,main_activity_note,main_activity_menu"
             app:flow_horizontalGap="6dp"
             app:flow_wrapMode="aligned"
             app:layout_constraintRight_toLeftOf="@id/main_activity_right_fragment"
@@ -90,7 +90,7 @@
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:visibility="invisible"
-            app:constraint_referenced_ids="main_activity_serach,main_activity_2d_3d,main_activity_camera,main_activity_trace,main_activity_calc_disance" />
+            app:constraint_referenced_ids="main_activity_serach,main_activity_2d_3d,main_activity_camera,main_activity_trace,main_activity_note,main_activity_calc_disance" />
 
         <ImageButton
             android:id="@+id/main_activity_serach"
@@ -122,6 +122,12 @@
             android:onClick="@{()->mainActivity.onClickCalcDisance()}"
             android:src="@drawable/icon_calc_disance" />
 
+        <ImageButton
+            android:id="@+id/main_activity_note"
+            style="@style/top_right_drawer_btns_style"
+            android:onClick="@{()->mainActivity.onClickNewNote()}"
+            android:src="@drawable/icon_calc_disance" />
+
         <ImageButton
             android:id="@+id/main_activity_menu"
             android:layout_width="@dimen/top_right_drawer_wh"
@@ -224,16 +230,6 @@
             app:layout_constraintLeft_toLeftOf="parent"
             app:layout_constraintTop_toTopOf="@id/main_activity_sign_recyclerview" />
 
-        <fragment
-            android:id="@+id/main_activity_right_fragment"
-            android:name="androidx.navigation.fragment.NavHostFragment"
-            android:layout_width="wrap_content"
-            android:layout_height="0dp"
-            android:elevation="3dp"
-            app:layout_constraintBottom_toBottomOf="parent"
-            app:layout_constraintRight_toRightOf="parent"
-            app:layout_constraintTop_toTopOf="parent"
-            app:navGraph="@navigation/right_fragment_nav_graph" />
 
         <TextView
             android:id="@+id/main_activity_geometry"
@@ -250,7 +246,18 @@
             android:text="经纬度:116.99388424,38.8403844"
             android:textSize="10sp"
             app:layout_constraintBottom_toTopOf="@id/main_activity_bottom_sheet_bg"
-            app:layout_constraintRight_toLeftOf="@id/main_activity_middle_fragment" />
+            app:layout_constraintRight_toLeftOf="@id/main_activity_right_fragment" />
+
+        <fragment
+            android:id="@+id/main_activity_right_fragment"
+            android:name="androidx.navigation.fragment.NavHostFragment"
+            android:layout_width="wrap_content"
+            android:layout_height="0dp"
+            android:elevation="4dp"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintRight_toRightOf="parent"
+            app:layout_constraintTop_toTopOf="parent"
+            app:navGraph="@navigation/right_fragment_nav_graph" />
 
         <TextView
             android:id="@+id/main_activity_road_name"
@@ -336,16 +343,21 @@
             android:layout_height="wrap_content"
             app:constraint_referenced_ids="main_activity_select_line,main_activity_voice,main_activity_add_new" />
 
+        <androidx.constraintlayout.widget.Group
+            android:id="@+id/main_activity_right_visibility_buttons_group2"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            app:constraint_referenced_ids="main_activity_select_line,main_activity_voice,main_activity_add_new,main_activity_zoom_in,main_activity_zoom_out,main_activity_geometry,main_activity_location" />
+
         <fragment
             android:id="@+id/main_activity_middle_fragment"
             android:name="androidx.navigation.fragment.NavHostFragment"
-            android:layout_width="wrap_content"
+            android:layout_width="0dp"
             android:layout_height="0dp"
-            android:layout_marginTop="6dp"
-            android:layout_marginRight="-1dp"
             app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintLeft_toLeftOf="parent"
             app:layout_constraintRight_toLeftOf="@id/main_activity_right_fragment"
-            app:layout_constraintTop_toBottomOf="@id/main_activity_flow"
+            app:layout_constraintTop_toTopOf="parent"
             app:navGraph="@navigation/middle_fragment_nav_graph" />
 
         <androidx.constraintlayout.widget.Group
diff --git a/app/src/main/res/layout/fragment_canvas.xml b/app/src/main/res/layout/fragment_canvas.xml
new file mode 100644
index 00000000..f9111456
--- /dev/null
+++ b/app/src/main/res/layout/fragment_canvas.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<com.navinfo.omqs.ui.fragment.note.CanvasView xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/canvas_view"
+    android:elevation="4dp"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="#94fcfbfb"
+    android:clickable="true" />
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_note.xml b/app/src/main/res/layout/fragment_note.xml
new file mode 100644
index 00000000..6ee54057
--- /dev/null
+++ b/app/src/main/res/layout/fragment_note.xml
@@ -0,0 +1,235 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="@dimen/right_fragment_w"
+    android:layout_height="match_parent"
+    android:background="@drawable/shape_right_fragment_bg"
+    tools:context=".ui.fragment.note.NoteFragment">
+
+    <TextView
+        android:id="@+id/note_bar"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginLeft="12dp"
+        android:layout_marginTop="14dp"
+        android:drawableLeft="@drawable/selector_btn_back_xml"
+        android:text="Mark"
+        android:textColor="@color/default_blue_text_color"
+        android:textSize="16sp"
+        app:layout_constraintLeft_toLeftOf="parent"
+        app:layout_constraintTop_toTopOf="parent" />
+
+    <ImageButton
+        android:id="@+id/note_bar_save"
+        android:layout_width="40dp"
+        android:layout_height="40dp"
+        android:layout_marginTop="9dp"
+        android:layout_marginRight="14dp"
+        android:background="@drawable/ripple_fragment_save_botton_bg"
+        android:src="@drawable/icon_save"
+        app:layout_constraintRight_toRightOf="parent"
+        app:layout_constraintTop_toTopOf="parent" />
+
+
+    <ImageButton
+        android:id="@+id/note_bar_cancel"
+        android:layout_width="40dp"
+        android:layout_height="40dp"
+        android:layout_marginRight="10dp"
+        android:background="@color/transparent"
+        android:src="@drawable/icon_fragment_close"
+        android:visibility="gone"
+        app:layout_constraintRight_toLeftOf="@id/note_bar_save"
+        app:layout_constraintTop_toTopOf="@id/note_bar_save" />
+
+
+    <ImageButton
+        android:id="@+id/note_bar_delete"
+        android:layout_width="40dp"
+        android:layout_height="40dp"
+        android:layout_centerInParent="true"
+        android:layout_marginRight="5dp"
+        android:background="@color/transparent"
+        android:src="@drawable/icon_delete"
+        app:layout_constraintRight_toLeftOf="@id/note_bar_cancel"
+        app:layout_constraintTop_toTopOf="@id/note_bar_save" />
+
+    <androidx.core.widget.NestedScrollView
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        android:layout_marginTop="6dp"
+        android:paddingLeft="11dp"
+        android:paddingTop="6dp"
+        android:paddingRight="9dp"
+        android:paddingBottom="6dp"
+        app:layout_constraintBottom_toTopOf="@id/note_camera"
+        app:layout_constraintLeft_toLeftOf="parent"
+        app:layout_constraintRight_toRightOf="parent"
+        app:layout_constraintTop_toBottomOf="@id/note_bar_save">
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:layout_marginBottom="7dp"
+            android:orientation="vertical">
+
+            <LinearLayout
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:gravity="center_vertical"
+                android:orientation="horizontal"
+                android:paddingTop="5dp"
+                android:paddingBottom="5dp">
+
+                <TextView
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="橡皮擦&#8195;" />
+
+                <ImageView
+                    android:id="@+id/sketch_eraser"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:background="@drawable/selector_sketch_eraser_bg"
+                    android:src="@drawable/sketch_eraser" />
+
+            </LinearLayout>
+
+            <LinearLayout
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="10dp"
+                android:orientation="horizontal">
+
+                <ImageView
+                    android:id="@+id/sketch_back"
+                    style="@style/sketch_operation_style"
+                    android:src="@drawable/sketch_back" />
+
+                <View
+                    style="@style/link_gray_style"
+                    android:layout_margin="2dp" />
+
+                <ImageView
+                    android:id="@+id/sketch_forward"
+                    style="@style/sketch_operation_style"
+                    android:src="@drawable/sketch_forward" />
+
+                <View
+                    style="@style/link_gray_style"
+                    android:layout_margin="2dp" />
+
+                <TextView
+                    android:id="@+id/sketch_clear"
+                    style="@style/sketch_operation_style"
+                    android:text="重绘" />
+            </LinearLayout>
+
+
+            <LinearLayout
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:orientation="horizontal">
+
+                <TextView
+                    style="@style/evaluation_fragment_title_text_style"
+                    android:layout_gravity="top"
+                    android:layout_marginTop="3dp"
+                    android:text="备注" />
+
+                <com.navinfo.omqs.ui.widget.MyEditeText
+                    android:id="@+id/note_description"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:background="@drawable/shape_rect_white_2dp_bg"
+                    android:elevation="2dp"
+                    android:gravity="start"
+                    android:hint="请输入备注信息"
+                    android:inputType="textMultiLine"
+                    android:lines="3"
+                    android:maxLines="3"
+                    android:paddingLeft="12dp"
+                    android:paddingTop="2dp"
+                    android:paddingBottom="2dp"
+                    android:textSize="12sp" />
+
+            </LinearLayout>
+
+
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginBottom="8dp"
+                android:text="多媒体"
+                android:textColor="@color/default_blue_text_color"
+                android:textSize="16sp" />
+
+            <!--
+            这种效果也好实现,主要的关键点是 Android:clipChildren=”false” 这个属性。
+1.配置ViewPager 和其父布局的 android:clipChildren属性为”false”.
+(android:clipChildren表示是否限制子View在其范围内,默认为true. 代码设置setClipChildren(false))
+因为如果clipChildren属性设置为true,就表明我们要将children给clip掉,就是说对于子元素来说,超出当前view的部分都会被切掉,那我们在这里把它设置成false,就表明超出view的部分,不要切掉,依然显示。
+注意:setClipChildren(false)在3.0以上版本中,开启了硬件加速后将不能正常工作,所以需要将其设置为软件加速。设置软硬件加速使用 setLayerType(View.LAYER_TYPE_SOFTWARE, null); 也可以在布局文件中添加 android:layerType=”software”
+
+             -->
+            <LinearLayout
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:gravity="center"
+                android:orientation="horizontal">
+
+                <ImageView
+                    android:id="@+id/note_picture_left"
+                    android:layout_width="24dp"
+                    android:layout_height="24dp"
+                    android:layout_marginRight="4dp"
+                    android:background="@drawable/icon_picture_left"
+                    android:padding="5dp" />
+
+                <androidx.viewpager2.widget.ViewPager2
+                    android:id="@+id/note_picture_viewpager"
+                    android:layout_width="wrap_content"
+                    android:layout_height="50dp"
+                    android:layout_weight="1"
+                    android:clipChildren="false" />
+
+                <ImageView
+                    android:id="@+id/note_picture_right"
+                    android:layout_width="24dp"
+                    android:layout_height="24dp"
+                    android:layout_marginLeft="4dp"
+                    android:background="@drawable/icon_picture_right"
+                    android:padding="5dp" />
+            </LinearLayout>
+
+            <androidx.recyclerview.widget.RecyclerView
+                android:id="@+id/note_voice_recyclerview"
+                android:layout_width="match_parent"
+                android:layout_height="120dp" />
+
+        </LinearLayout>
+    </androidx.core.widget.NestedScrollView>
+
+    <ImageView
+        android:id="@+id/note_camera"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:padding="10dp"
+        android:src="@drawable/baseline_camera_alt_24"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintHorizontal_weight="1"
+        app:layout_constraintLeft_toLeftOf="parent"
+        app:layout_constraintRight_toLeftOf="@id/note_voice" />
+
+    <ImageView
+        android:id="@+id/note_voice"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:padding="10dp"
+        android:src="@drawable/baseline_keyboard_voice_24"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintHorizontal_weight="1"
+        app:layout_constraintLeft_toRightOf="@id/note_camera"
+        app:layout_constraintRight_toRightOf="parent" />
+</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_phenomenon.xml b/app/src/main/res/layout/fragment_phenomenon.xml
index 47d18747..73c73788 100644
--- a/app/src/main/res/layout/fragment_phenomenon.xml
+++ b/app/src/main/res/layout/fragment_phenomenon.xml
@@ -1,26 +1,32 @@
 <?xml version="1.0" encoding="utf-8"?>
-<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     xmlns:tools="http://schemas.android.com/tools"
-    android:layout_width="@dimen/fragment_phenomenon_width"
+    android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:background="@drawable/shape_middle_fragment_bg"
+    android:gravity="right"
     tools:context="com.navinfo.omqs.ui.fragment.evaluationresult.PhenomenonFragment">
 
-    <androidx.recyclerview.widget.RecyclerView
-        android:id="@+id/phenomenon_left_recyclerview"
-        android:layout_width="148dp"
-        android:layout_height="0dp"
-        app:layout_constraintBottom_toBottomOf="parent"
-        app:layout_constraintLeft_toLeftOf="parent"
-        app:layout_constraintTop_toTopOf="parent" />
+    <LinearLayout
+        android:layout_width="@dimen/fragment_phenomenon_width"
+        android:layout_height="match_parent"
+        android:layout_marginTop="55dp"
+        android:background="@drawable/shape_middle_fragment_bg"
+        android:orientation="horizontal">
 
-    <androidx.recyclerview.widget.RecyclerView
-        android:id="@+id/phenomenon_right_recyclerview"
-        android:layout_width="0dp"
-        android:layout_height="0dp"
-        app:layout_constraintBottom_toBottomOf="parent"
-        app:layout_constraintLeft_toRightOf="@id/phenomenon_left_recyclerview"
-        app:layout_constraintRight_toRightOf="parent"
-        app:layout_constraintTop_toTopOf="parent" />
-</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
+        <androidx.recyclerview.widget.RecyclerView
+            android:id="@+id/phenomenon_left_recyclerview"
+            android:layout_width="148dp"
+            android:layout_height="match_parent"
+            android:layout_toLeftOf="@id/phenomenon_right_recyclerview" />
+
+        <androidx.recyclerview.widget.RecyclerView
+            android:id="@+id/phenomenon_right_recyclerview"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:layout_alignParentRight="true" />
+
+    </LinearLayout>
+
+
+</LinearLayout>
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_problem_link.xml b/app/src/main/res/layout/fragment_problem_link.xml
index d31f06f3..508cfe3e 100644
--- a/app/src/main/res/layout/fragment_problem_link.xml
+++ b/app/src/main/res/layout/fragment_problem_link.xml
@@ -2,13 +2,15 @@
 <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     xmlns:tools="http://schemas.android.com/tools"
-    android:layout_width="@dimen/fragment_problem_link_width"
+    android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:background="@drawable/shape_middle_fragment_bg"
     tools:context="com.navinfo.omqs.ui.fragment.evaluationresult.ProblemLinkFragment">
 
     <androidx.recyclerview.widget.RecyclerView
+        android:layout_marginTop="55dp"
         android:id="@+id/link_right_recyclerview"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent" />
+        android:layout_width="@dimen/fragment_problem_link_width"
+        android:layout_height="match_parent"
+        android:background="@drawable/shape_middle_fragment_bg"
+        app:layout_constraintRight_toRightOf="parent" />
 </androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/app/src/main/res/navigation/middle_fragment_nav_graph.xml b/app/src/main/res/navigation/middle_fragment_nav_graph.xml
index ef0d0a1d..b1897830 100644
--- a/app/src/main/res/navigation/middle_fragment_nav_graph.xml
+++ b/app/src/main/res/navigation/middle_fragment_nav_graph.xml
@@ -20,4 +20,10 @@
         android:name="com.navinfo.omqs.ui.fragment.evaluationresult.ProblemLinkFragment"
         android:label="评测页面"
         tools:layout="@layout/fragment_problem_link"></fragment>
+    <fragment
+        android:id="@+id/CanvasFragment"
+        android:name="com.navinfo.omqs.ui.fragment.note.CanvasFragment"
+        android:label="绘图页面"
+        tools:layout="@layout/fragment_canvas"></fragment>
+
 </navigation>
\ No newline at end of file
diff --git a/app/src/main/res/navigation/right_fragment_nav_graph.xml b/app/src/main/res/navigation/right_fragment_nav_graph.xml
index 7aa23dc4..b47ba040 100644
--- a/app/src/main/res/navigation/right_fragment_nav_graph.xml
+++ b/app/src/main/res/navigation/right_fragment_nav_graph.xml
@@ -30,4 +30,11 @@
         tools:layout="@layout/fragment_evaluation_result">
 
     </fragment>
+    <fragment
+        android:id="@+id/NoteFragment"
+        android:name="com.navinfo.omqs.ui.fragment.note.NoteFragment"
+        android:label="便签页面"
+        tools:layout="@layout/fragment_note">
+
+    </fragment>
 </navigation>
\ No newline at end of file
diff --git a/app/src/main/res/values/styleable.xml b/app/src/main/res/values/styleable.xml
new file mode 100644
index 00000000..45612391
--- /dev/null
+++ b/app/src/main/res/values/styleable.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+    <declare-styleable name="CanvasView">
+        <attr name="isSavePoint" format="boolean" />
+    </declare-styleable>
+</resources>
\ No newline at end of file
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index 5193314e..7775f0a2 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -1,5 +1,20 @@
 <resources>
 
+    <style name="sketch_operation_style">
+        <item name="android:layout_width">0dp</item>
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:layout_weight">1</item>
+        <item name="android:gravity">center</item>
+        <item name="android:paddingTop">5dp</item>
+        <item name="android:paddingBottom">5dp</item>
+    </style>
+
+    <style name="link_gray_style" comment="竖着的灰色分隔线">
+        <item name="android:layout_width">1dp</item>
+        <item name="android:layout_height">match_parent</item>
+        <item name="android:background">#C3C3C3</item>
+    </style>
+
     <style name="main_activity_bottom_sheet_icon" comment="主控页面下方按钮样式">
         <item name="android:layout_width">36dp</item>
         <item name="android:layout_height">36dp</item>
diff --git a/collect-library/build.gradle b/collect-library/build.gradle
index 6b7ed9ba..ca7564d3 100644
--- a/collect-library/build.gradle
+++ b/collect-library/build.gradle
@@ -79,7 +79,7 @@ dependencies {
     implementation "net.sf.kxml:kxml2:2.3.0"
     implementation 'org.slf4j:slf4j-api:2.0.7'
     implementation project(":vtm-themes")
-    api project(":vtm-android")
+    implementation project(":vtm-android")
     implementation project(':vtm-extras')
     implementation project(":vtm-http")
 //    implementation "org.mapsforge:vtm-themes:$vtmVersion"
diff --git a/collect-library/src/main/java/com/navinfo/collect/library/data/entity/NoteBean.kt b/collect-library/src/main/java/com/navinfo/collect/library/data/entity/NoteBean.kt
new file mode 100644
index 00000000..1d9959b1
--- /dev/null
+++ b/collect-library/src/main/java/com/navinfo/collect/library/data/entity/NoteBean.kt
@@ -0,0 +1,13 @@
+package com.navinfo.collect.library.data.entity
+
+import io.realm.RealmList
+import io.realm.RealmObject
+import io.realm.annotations.PrimaryKey
+
+open class NoteBean @JvmOverloads constructor(
+    @PrimaryKey
+    var id: String = "",
+    var guideGeometry: String = "",
+    var description: String = "",
+    var list: RealmList<SketchAttachContent> = RealmList<SketchAttachContent>(),
+) : RealmObject()
\ No newline at end of file
diff --git a/collect-library/src/main/java/com/navinfo/collect/library/data/entity/SketchAttachContent.kt b/collect-library/src/main/java/com/navinfo/collect/library/data/entity/SketchAttachContent.kt
new file mode 100644
index 00000000..b4e3fe43
--- /dev/null
+++ b/collect-library/src/main/java/com/navinfo/collect/library/data/entity/SketchAttachContent.kt
@@ -0,0 +1,40 @@
+package com.navinfo.collect.library.data.entity
+
+import io.realm.RealmObject
+import io.realm.annotations.PrimaryKey
+
+/**
+ * @author zhjch
+ * @version V1.0
+ * @ClassName: SketchAttachContent
+ * @Date 2016/5/19
+ * @Description: ${TODO}(草图内容 )
+ */
+open class SketchAttachContent @JvmOverloads constructor(
+    @PrimaryKey
+    var id: String = "",
+    /**
+     * 获取geo
+     *
+     * @return geo
+     */
+    /**
+     * 设置geo
+     *
+     * @param geo geo
+     */
+    //几何
+    var geometry: String = "",
+    /**
+     * 获取style
+     *
+     * @return style
+     */
+    /**
+     * 设置style
+     *
+     * @param style style
+     */
+    //样式
+    var style: String = ""
+) : RealmObject()
\ No newline at end of file
diff --git a/collect-library/src/main/java/com/navinfo/collect/library/map/handler/BaseHandler.kt b/collect-library/src/main/java/com/navinfo/collect/library/map/handler/BaseHandler.kt
index 4f3d1274..6e3240a0 100644
--- a/collect-library/src/main/java/com/navinfo/collect/library/map/handler/BaseHandler.kt
+++ b/collect-library/src/main/java/com/navinfo/collect/library/map/handler/BaseHandler.kt
@@ -20,6 +20,8 @@ abstract class BaseHandler(context: AppCompatActivity, mapView: NIMapView) {
         mMapView.vtmMap.layers().remove(layer)
     }
 
+
+
 //    fun setOnMapClickListener(listener: NIMapView.OnMapClickListener) {
 //        mMapView.setOnMapClickListener(listener)
 //    }
diff --git a/collect-library/src/main/java/com/navinfo/collect/library/map/handler/LineHandler.kt b/collect-library/src/main/java/com/navinfo/collect/library/map/handler/LineHandler.kt
index 6eea65b3..68bff116 100644
--- a/collect-library/src/main/java/com/navinfo/collect/library/map/handler/LineHandler.kt
+++ b/collect-library/src/main/java/com/navinfo/collect/library/map/handler/LineHandler.kt
@@ -8,10 +8,10 @@ import androidx.appcompat.app.AppCompatActivity
 import com.navinfo.collect.library.R
 import com.navinfo.collect.library.map.NIMapView
 import com.navinfo.collect.library.map.layers.MultiLinesLayer
+import com.navinfo.collect.library.map.layers.NoteLineLayer
 import com.navinfo.collect.library.map.layers.OmdbTaskLinkLayer
 import com.navinfo.collect.library.utils.GeometryTools
 import com.navinfo.collect.library.utils.StringUtil
-import org.locationtech.jts.geom.LineString
 import org.oscim.android.canvas.AndroidBitmap
 import org.oscim.backend.canvas.Bitmap
 import org.oscim.core.GeoPoint
@@ -23,7 +23,6 @@ import org.oscim.layers.marker.MarkerInterface
 import org.oscim.layers.marker.MarkerItem
 import org.oscim.layers.marker.MarkerSymbol
 import org.oscim.layers.vector.PathLayer
-import org.oscim.layers.vector.VectorLayer
 import org.oscim.layers.vector.geometries.Style
 import org.oscim.map.Map
 
@@ -69,6 +68,9 @@ class LineHandler(context: AppCompatActivity, mapView: NIMapView) : BaseHandler(
         layer
     }
 
+    /**
+     * 任务线图层
+     */
     val omdbTaskLinkLayer by lazy {
         val omdbTaskLinkLayer = OmdbTaskLinkLayer(
             mMapView.vtmMap,
@@ -84,6 +86,7 @@ class LineHandler(context: AppCompatActivity, mapView: NIMapView) : BaseHandler(
         omdbTaskLinkLayer
     }
 
+
     init {
         mMapView.vtmMap.events.bind(this)
 
diff --git a/collect-library/src/main/java/com/navinfo/collect/library/map/handler/MarkHandler.kt b/collect-library/src/main/java/com/navinfo/collect/library/map/handler/MarkHandler.kt
index e4e9b588..4ba05831 100644
--- a/collect-library/src/main/java/com/navinfo/collect/library/map/handler/MarkHandler.kt
+++ b/collect-library/src/main/java/com/navinfo/collect/library/map/handler/MarkHandler.kt
@@ -4,16 +4,17 @@ import android.content.Context
 import android.graphics.BitmapFactory
 import android.graphics.Canvas
 import android.graphics.Color
-import android.util.Log
 import androidx.appcompat.app.AppCompatActivity
 import androidx.core.content.res.ResourcesCompat
 import androidx.lifecycle.lifecycleScope
 import com.navinfo.collect.library.R
+import com.navinfo.collect.library.data.entity.NoteBean
 import com.navinfo.collect.library.data.entity.QsRecordBean
 import com.navinfo.collect.library.map.NIMapView
 import com.navinfo.collect.library.map.cluster.ClusterMarkerItem
 import com.navinfo.collect.library.map.cluster.ClusterMarkerRenderer
 import com.navinfo.collect.library.map.layers.MyItemizedLayer
+import com.navinfo.collect.library.map.layers.NoteLineLayer
 import com.navinfo.collect.library.utils.GeometryTools
 import com.navinfo.collect.library.utils.StringUtil
 import io.realm.Realm
@@ -22,6 +23,8 @@ import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.withContext
 import org.locationtech.jts.geom.Geometry
+import org.locationtech.jts.geom.LineString
+import org.locationtech.jts.geom.Polygon
 import org.oscim.android.canvas.AndroidBitmap
 import org.oscim.backend.CanvasAdapter
 import org.oscim.backend.canvas.Bitmap
@@ -29,9 +32,9 @@ import org.oscim.backend.canvas.Paint
 import org.oscim.core.GeoPoint
 import org.oscim.layers.marker.*
 import org.oscim.layers.marker.ItemizedLayer.OnItemGestureListener
+import org.oscim.layers.vector.geometries.*
 import org.oscim.map.Map
 import java.util.*
-import kotlin.collections.ArrayList
 
 /**
  * marker 操作
@@ -56,8 +59,10 @@ class MarkHandler(context: AppCompatActivity, mapView: NIMapView) :
     //画布
     private lateinit var canvas: org.oscim.backend.canvas.Canvas
     private lateinit var itemizedLayer: MyItemizedLayer
+
     private lateinit var markerRendererFactory: MarkerRendererFactory
-    private var resId = R.mipmap.map_icon_report
+    private val resId = R.mipmap.map_icon_report
+    private val noteResId = R.drawable.icon_note_marker
     private var itemListener: OnQsRecordItemClickListener? = null
 
     /**
@@ -65,6 +70,56 @@ class MarkHandler(context: AppCompatActivity, mapView: NIMapView) :
      */
     private val NUM_13 = 13
 
+    /**
+     * 便签线图层
+     */
+    private val noteLineLayer: NoteLineLayer by lazy {
+        val layer = NoteLineLayer(mMapView.vtmMap)
+        addLayer(layer, NIMapView.LAYER_GROUPS.VECTOR)
+        layer
+    }
+
+
+    private val noteLayer: MyItemizedLayer by lazy {
+
+        val layer = MyItemizedLayer(
+            mMapView.vtmMap,
+            mutableListOf(),
+            markerRendererFactory,
+            object : MyItemizedLayer.OnItemGestureListener {
+                override fun onItemSingleTapUp(
+                    list: MutableList<Int>,
+                    nearest: Int
+                ): Boolean {
+                    itemListener?.let {
+                        val idList = mutableListOf<String>()
+                        if (list.size == 0) {
+                        } else {
+                            for (i in list) {
+                                val markerInterface: MarkerInterface =
+                                    noteLayer.itemList[i]
+                                if (markerInterface is MarkerItem) {
+                                    idList.add(markerInterface.title)
+                                }
+                            }
+                            it.onNoteList(idList.distinct().toMutableList())
+                        }
+                    }
+                    return true
+                }
+
+                override fun onItemLongPress(
+                    list: MutableList<Int>?,
+                    nearest: Int
+                ): Boolean {
+                    return true
+                }
+            })
+
+        addLayer(layer, NIMapView.LAYER_GROUPS.OPERATE_MARKER)
+        layer
+    }
+
     init {
         //新增marker图标样式
         val mDefaultBitmap =
@@ -100,6 +155,7 @@ class MarkHandler(context: AppCompatActivity, mapView: NIMapView) :
                 itemizedLayer.isEnabled = mapPosition.getZoomLevel() >= 12
             }
         })
+        initNoteData()
         mMapView.updateMap()
     }
 
@@ -141,6 +197,9 @@ class MarkHandler(context: AppCompatActivity, mapView: NIMapView) :
         }
     }
 
+    /**
+     * 移除marker
+     */
     fun removeMarker(title: String) {
         var marker: MarkerItem? = null
         for (e in mDefaultMarkerLayer.itemList) {
@@ -172,12 +231,53 @@ class MarkHandler(context: AppCompatActivity, mapView: NIMapView) :
         withContext(Dispatchers.Main) {
             mMapView.updateMap(true)
         }
-
     }
 
 
     /**
-     * 删除marker
+     * 增加或更新便签
+     */
+    suspend fun addOrUpdateNoteMark(data: NoteBean) {
+        for (item in noteLayer.itemList) {
+            if (item is MarkerItem) {
+                if (item.title == data.id) {
+                    noteLayer.itemList.remove(item)
+                    break
+                }
+            }
+        }
+        noteLineLayer.removeNoteBeanLines(data)
+        createNoteMarkerItem(data)
+        withContext(Dispatchers.Main) {
+            mMapView.updateMap(true)
+        }
+    }
+
+
+    private fun convertGeometry2Drawable(geometry: Geometry, vectorLayerStyle: Style): Drawable? {
+        var resultDrawable: Drawable? = null
+        if ("POINT" == geometry.geometryType.uppercase(Locale.getDefault())) {
+            val geoPoint = GeoPoint(geometry.coordinate.y, geometry.coordinate.x)
+            if (geoPoint != null) {
+                resultDrawable = PointDrawable(geoPoint, vectorLayerStyle)
+            }
+        } else if ("LINESTRING" == geometry.geometryType.uppercase(Locale.getDefault())) {
+            val lineString = geometry as LineString
+            if (lineString != null) {
+                resultDrawable = LineDrawable(lineString, vectorLayerStyle)
+            }
+        } else if ("POLYGON" == geometry.geometryType.uppercase(Locale.getDefault())) {
+            val polygon = geometry as Polygon
+            if (polygon != null) {
+                resultDrawable = PolygonDrawable(polygon, vectorLayerStyle)
+            }
+        }
+        return resultDrawable
+    }
+
+
+    /**
+     * 删除质检数据
      */
     suspend fun removeQsRecordMark(data: QsRecordBean) {
         for (item in itemizedLayer.itemList) {
@@ -191,6 +291,43 @@ class MarkHandler(context: AppCompatActivity, mapView: NIMapView) :
         }
     }
 
+    /**
+     * 删除标签
+     */
+    suspend fun removeNoteMark(data: NoteBean) {
+        for (item in noteLayer.itemList) {
+            if (item is MarkerItem) {
+                if (item.title == data.id) {
+                    noteLayer.itemList.remove(item)
+                    noteLineLayer.removeNoteBeanLines(data)
+                    noteLayer.populate()
+                    withContext(Dispatchers.Main) {
+                        mMapView.updateMap(true)
+                    }
+                    return
+                }
+            }
+        }
+    }
+
+    /**
+     * 初始化便签
+     */
+    private fun initNoteData() {
+        mContext.lifecycleScope.launch(Dispatchers.IO) {
+            var list = mutableListOf<NoteBean>()
+            val realm = Realm.getDefaultInstance()
+            realm.executeTransaction {
+                val objects = realm.where<NoteBean>().findAll()
+                list = realm.copyFromRealm(objects)
+            }
+
+            for (item in list) {
+                createNoteMarkerItem(item)
+            }
+        }
+    }
+
     /**
      * 初始话质检数据图层
      */
@@ -276,6 +413,39 @@ class MarkHandler(context: AppCompatActivity, mapView: NIMapView) :
 
     }
 
+    /**
+     * 添加质检数据marker
+     */
+    private suspend fun createNoteMarkerItem(item: NoteBean) {
+        val bitmap: Bitmap = createTextMarkerBitmap(mContext, item.description, noteResId)
+        val geometry: Geometry? = GeometryTools.createGeometry(item.guideGeometry)
+        if (geometry != null) {
+            var geoPoint: org.oscim.core.GeoPoint? = null
+            if (geometry.geometryType != null) {
+                when (geometry.geometryType.uppercase(Locale.getDefault())) {
+                    "POINT" -> geoPoint =
+                        org.oscim.core.GeoPoint(geometry.coordinate.y, geometry.coordinate.x)
+                }
+            }
+            if (geoPoint != null) {
+                var geoMarkerItem: MarkerItem
+                geoMarkerItem = ClusterMarkerItem(
+                    1, item.id, item.description, geoPoint
+                )
+                val markerSymbol =
+                    MarkerSymbol(bitmap, MarkerSymbol.HotspotPlace.CENTER)
+                geoMarkerItem.marker = markerSymbol
+                noteLayer.itemList.add(geoMarkerItem)
+            }
+        }
+        noteLineLayer.showNoteBeanLines(item)
+        noteLayer.populate()
+    }
+
+
+    /**
+     * 添加质检数据marker
+     */
     private suspend fun createMarkerItem(item: QsRecordBean) {
         val bitmap: Bitmap = createTextMarkerBitmap(mContext, item.description, resId)
         if (item.t_lifecycle != 2) {
@@ -554,4 +724,5 @@ class MarkHandler(context: AppCompatActivity, mapView: NIMapView) :
 
 interface OnQsRecordItemClickListener {
     fun onQsRecordList(list: MutableList<String>)
+    fun onNoteList(list: MutableList<String>)
 }
diff --git a/collect-library/src/main/java/com/navinfo/collect/library/map/handler/ViewportHandler.kt b/collect-library/src/main/java/com/navinfo/collect/library/map/handler/ViewportHandler.kt
index 3f8e9e29..443fcc7e 100644
--- a/collect-library/src/main/java/com/navinfo/collect/library/map/handler/ViewportHandler.kt
+++ b/collect-library/src/main/java/com/navinfo/collect/library/map/handler/ViewportHandler.kt
@@ -1,13 +1,13 @@
 package com.navinfo.collect.library.map.handler
 
-import android.content.Context
+import android.graphics.Point
 import androidx.appcompat.app.AppCompatActivity
 import com.navinfo.collect.library.map.NIMapView
 import com.navinfo.collect.library.utils.GeometryTools
 import org.oscim.core.GeoPoint
-import org.oscim.core.Point
 
-open class ViewportHandler(context: AppCompatActivity, mapView: NIMapView) : BaseHandler(context, mapView) {
+open class ViewportHandler(context: AppCompatActivity, mapView: NIMapView) :
+    BaseHandler(context, mapView) {
     /**
      * Set pivot horizontal / vertical relative to view center in [-1, 1].
      * e.g. pivotY 0.5 is usually preferred for navigation, moving center to 25% of view height.
@@ -54,32 +54,47 @@ open class ViewportHandler(context: AppCompatActivity, mapView: NIMapView) : Bas
      * @param snapType 扩展外接矩形的方式,用屏幕像素还是距离
      *  @param distance 距离大小 像素 或 米
      */
+//    fun toScreenPoint(
+//        geoPoint: GeoPoint
+//    ): String {
+//        val point = Point()
+//
+//        mMapView.vtmMap.viewport().toScreenPoint(geoPoint, false, point)
+//
+//        return "${point.x},${point.y}"
+//    }
+
     fun toScreenPoint(
         geoPoint: GeoPoint
-    ): String {
-        val point = Point()
+    ): Point {
+        val point = org.oscim.core.Point()
 
         mMapView.vtmMap.viewport().toScreenPoint(geoPoint, false, point)
 
-        return "${point.x},${point.y}"
+        return Point(point.x.toInt(), point.y.toInt())
     }
 
-    /**
-     * 获取几何的外接矩形,返回矩形的左上,右下两个坐标
-     * @param snapType 扩展外接矩形的方式,用屏幕像素还是距离
-     *  @param distance 距离大小 像素 或 米
-     */
-    fun fromScreenPoint(
-        px: Float, py: Float
-    ): Map<String, Any> {
 
-        val geo = mMapView.vtmMap.viewport().fromScreenPoint(px, py)
+//    /**
+//     * 获取几何的外接矩形,返回矩形的左上,右下两个坐标
+//     * @param snapType 扩展外接矩形的方式,用屏幕像素还是距离
+//     *  @param distance 距离大小 像素 或 米
+//     */
+//    fun fromScreenPointMap(
+//        px: Float, py: Float
+//    ): Map<String, Any> {
+//
+//        val geo = mMapView.vtmMap.viewport().fromScreenPoint(px, py)
+//
+//        return mapOf(
+//            "latitude" to geo.latitude,
+//            "longitude" to geo.longitude,
+//            "longitudeE6" to geo.longitudeE6,
+//            "latitudeE6" to geo.latitudeE6,
+//        )
+//    }
 
-        return mapOf(
-            "latitude" to geo.latitude,
-            "longitude" to geo.longitude,
-            "longitudeE6" to geo.longitudeE6,
-            "latitudeE6" to geo.latitudeE6,
-        )
+    fun fromScreenPoint(point: android.graphics.Point): GeoPoint {
+        return mMapView.vtmMap.viewport().fromScreenPoint(point.x.toFloat(), point.y.toFloat())
     }
 }
\ No newline at end of file
diff --git a/collect-library/src/main/java/com/navinfo/collect/library/map/layers/NoteLineLayer.kt b/collect-library/src/main/java/com/navinfo/collect/library/map/layers/NoteLineLayer.kt
new file mode 100644
index 00000000..7a309187
--- /dev/null
+++ b/collect-library/src/main/java/com/navinfo/collect/library/map/layers/NoteLineLayer.kt
@@ -0,0 +1,98 @@
+package com.navinfo.collect.library.map.layers
+
+import android.graphics.Color
+import com.navinfo.collect.library.data.entity.NoteBean
+import com.navinfo.collect.library.utils.GeometryTools
+import org.oscim.layers.vector.VectorLayer
+import org.oscim.layers.vector.geometries.Drawable
+import org.oscim.layers.vector.geometries.LineDrawable
+import org.oscim.layers.vector.geometries.Style
+import org.oscim.map.Map
+
+
+class NoteLineLayer(map: Map) : VectorLayer(map) {
+    private val lineMap = HashMap<String, MutableList<Drawable>>()
+
+
+    private var selectDrawable: Drawable? = null
+
+    private val selectStyle =
+        Style.builder().fillColor(Color.GREEN).strokeColor(Color.GREEN)
+            .strokeWidth(10f).fixed(false).build()
+
+
+    @Synchronized
+    fun showNoteBeanLines(noteBean: NoteBean) {
+        removeNoteBeanLines(noteBean)
+        val list = mutableListOf<Drawable>()
+        for (item in noteBean.list) {
+            val lineDrawable =
+                LineDrawable(GeometryTools.createGeometry(item.geometry), getStyle(item.style))
+            add(lineDrawable)
+            list.add(lineDrawable)
+        }
+        lineMap[noteBean.id] = list
+        update()
+    }
+
+
+    @Synchronized
+    fun removeNoteBeanLines(noteBean: NoteBean) {
+        if (lineMap.containsKey(noteBean.id)) {
+            for (drawable in lineMap[noteBean.id]!!) {
+                remove(drawable)
+            }
+            lineMap.remove(noteBean.id)
+        }
+        update()
+    }
+
+
+    private fun getStyle(style: String): Style {
+//        if (style.startsWith("4")) {
+//            canvasStyle = CanvasView.CanvasStyle.RAILWAY_LINE
+//        } else if (style.startsWith("5")) {
+//            if (style.contains("cde3ac")) {
+//                canvasStyle = CanvasView.CanvasStyle.GREENLAND_LINE
+//            } else if (style.contains("abcaff")) {
+//                canvasStyle = CanvasView.CanvasStyle.WATER_LINE
+//            } else if (style.contains("fffe98")) {
+//                canvasStyle = CanvasView.CanvasStyle.PARKING_LINE
+//            }
+//        } else {
+//            val s: String = style.substring(0, 1)
+//            if (TextUtils.equals(s, "2")) {
+//                canvasStyle = CanvasView.CanvasStyle.STRAIGHT_LINE
+//            } else if (TextUtils.equals(s, "3")) {
+//                canvasStyle = CanvasView.CanvasStyle.RECT_LINE
+//            } else if (TextUtils.equals(s, "6")) {
+//                canvasStyle = CanvasView.CanvasStyle.POLY_LINE
+//            } else if (TextUtils.equals(s, "7")) {
+//                canvasStyle = CanvasView.CanvasStyle.ELLIPSE_LINE
+//            } else if (TextUtils.equals(s, "9")) {
+//                canvasStyle = CanvasView.CanvasStyle.CIRCULAR_POINT
+//            } else if (TextUtils.equals(s, "1")) {
+//                canvasStyle = CanvasView.CanvasStyle.FREE_LINE
+//            }
+        val width = style.substring(1, 3).toFloat()
+        var colorStr: String = style.substring(3, style.length)
+        colorStr = if (colorStr.length == 6) {
+            "#ff$colorStr"
+        } else {
+            "#ff000000"
+        }
+//        val color = colorStr.toLong(16).toInt()
+        return Style.builder().fillColor(colorStr).fillAlpha(0.5f).strokeColor(colorStr)
+            .strokeWidth(width).fixed(true).build()
+    }
+
+    fun removeAll() {
+        for ((_, value) in lineMap) {
+            for (item in value) {
+                remove(item)
+            }
+        }
+        lineMap.clear()
+        update()
+    }
+}
\ No newline at end of file
diff --git a/collect-library/src/main/res/drawable/icon_note_marker.xml b/collect-library/src/main/res/drawable/icon_note_marker.xml
new file mode 100644
index 00000000..9add4f3e
--- /dev/null
+++ b/collect-library/src/main/res/drawable/icon_note_marker.xml
@@ -0,0 +1,12 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="54.723"
+    android:viewportHeight="40">
+  <path
+      android:pathData="M7.442,28.743S26.236,7.002 42.58,4.502C23.513,11.5 1.721,39.987 1.721,39.987L22.968,39.987a1.589,1.589 0,0 0,-1.631 -1L4.445,38.987A78.915,78.915 0,0 1,10.438 33.243c10.9,-0.5 20.43,-1.5 26.968,-10 1.089,-2 -0.815,-1.749 -1.362,-1.749 5.176,-2 16.344,-9.495 14.709,-13.245 -0.815,-0.748 -1.362,-0.251 -1.362,-0.251s8.444,-5 4.077,-7.247S38.214,2.003 38.214,2.003s0.815,-1.5 -1.631,-1.5A31.679,31.679 0,0 0,20.521 7.002s0,-2 -1.362,-1.249C0.359,16.497 7.442,28.743 7.442,28.743Z"
+      android:fillColor="#EA6B00"/>
+  <path
+      android:pathData="M4.6,35.624a2.016,2.016 0,0 0,-1.157 -0.481,2.3 2.3,0 0,0 -1.19,0.164 4.085,4.085 0,0 0,-1.3 1.189,4.031 4.031,0 0,0 -0.8,1.174s-0.478,1.749 0.229,2.1a3.616,3.616 0,0 0,2.484 0.067,4.594 4.594,0 0,0 1.373,-0.5 2.832,2.832 0,0 0,0.973 -0.978A2.291,2.291 0,0 0,4.6 35.624Z"
+      android:fillColor="#EA6B00"/>
+</vector>
diff --git a/settings.gradle b/settings.gradle
index edc00b4f..b309d4f9 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1,23 +1,21 @@
 pluginManagement {
     repositories {
-        google()
-        gradlePluginPortal()
-        mavenCentral()
+
         maven { url "https://maven.aliyun.com/repository/google" }
         maven { url 'https://maven.aliyun.com/repository/gradle-plugin' }
         maven { url 'https://maven.aliyun.com/repository/public' }
         maven { url 'https://maven.aliyun.com/repository/jcenter' }
         maven { url 'https://maven.aliyun.com/repository/central' }
         maven { url 'https://jitpack.io' }
+        mavenCentral()
+        google()
+        gradlePluginPortal()
     }
 }
 dependencyResolutionManagement {
     repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
     repositories {
 
-        google()
-        mavenCentral()
-        gradlePluginPortal()
 
         maven { url "https://maven.aliyun.com/repository/google" }
         maven { url 'https://maven.aliyun.com/repository/gradle-plugin' }
@@ -26,6 +24,9 @@ dependencyResolutionManagement {
         maven { url 'https://maven.aliyun.com/repository/central' }
         maven { url 'https://jitpack.io' }
 
+        mavenCentral()
+        google()
+        gradlePluginPortal()
 
     }
 }