From 0b700614e2f3e41df4655ba5469217e009c246ac Mon Sep 17 00:00:00 2001
From: riku <risaku@163.com>
Date: 星期一, 24 二月 2025 17:09:13 +0800
Subject: [PATCH] 1. 初步完成走航融合相关功能

---
 src/views/satellitetelemetry/component/SatelliteDataMix.vue |    2 
 src/views/satellitetelemetry/SatelliteTelemetry.vue         |   99 +++--
 src/utils/map/grid.js                                       |  194 +++++++---
 src/components.d.ts                                         |    4 
 src/views/HomePage.vue                                      |   18 
 src/views/satellitetelemetry/SatelliteProxy.js              |  135 ++++++-
 src/views/satellitetelemetry/component/SatelliteMixTool.vue |  311 ++++++++++++++++++
 src/stores/satellite-grid.js                                |    7 
 src/views/satellitetelemetry/component/SatelliteManage.vue  |   84 +++-
 src/api/index.js                                            |    2 
 src/components/animation/SatelliteAnimation.vue             |    5 
 src/views/satellitetelemetry/SatelliteTelemetry copy.vue    |    5 
 src/stores/mission.js                                       |    2 
 src/utils/map/line.js                                       |   98 +++++
 14 files changed, 798 insertions(+), 168 deletions(-)

diff --git a/src/api/index.js b/src/api/index.js
index e90dd06..a87b389 100644
--- a/src/api/index.js
+++ b/src/api/index.js
@@ -10,7 +10,7 @@
 }
 
 if (debug) {
-  ip1 = 'http://192.168.0.138:8084/';
+  ip1 = 'http://192.168.0.110:8084/';
 }
 
 const $http = axios.create({
diff --git a/src/components.d.ts b/src/components.d.ts
index fe15cfb..3789168 100644
--- a/src/components.d.ts
+++ b/src/components.d.ts
@@ -34,10 +34,14 @@
     ElIcon: typeof import('element-plus/es')['ElIcon']
     ElInput: typeof import('element-plus/es')['ElInput']
     ElOption: typeof import('element-plus/es')['ElOption']
+    ElPagination: typeof import('element-plus/es')['ElPagination']
     ElPopover: typeof import('element-plus/es')['ElPopover']
+    ElRadio: typeof import('element-plus/es')['ElRadio']
+    ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup']
     ElRow: typeof import('element-plus/es')['ElRow']
     ElScrollbar: typeof import('element-plus/es')['ElScrollbar']
     ElSelect: typeof import('element-plus/es')['ElSelect']
+    ElSlider: typeof import('element-plus/es')['ElSlider']
     ElTable: typeof import('element-plus/es')['ElTable']
     ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
     ElText: typeof import('element-plus/es')['ElText']
diff --git a/src/components/animation/SatelliteAnimation.vue b/src/components/animation/SatelliteAnimation.vue
index bc0845f..5680a41 100644
--- a/src/components/animation/SatelliteAnimation.vue
+++ b/src/components/animation/SatelliteAnimation.vue
@@ -109,7 +109,10 @@
           that.mapViews.textViews = textViews;
           // eslint-disable-next-line vue/no-mutating-props
           that.mapViews.labelsLayer = labelsLayer;
-          SatelliteProxy.drawColor(that.mapViews.gridViews, d);
+          SatelliteProxy.drawColor({
+            gridViews: that.mapViews.gridViews,
+            gridDataDetail: d
+          });
         }
       );
     },
diff --git a/src/stores/mission.js b/src/stores/mission.js
index 37c2871..b90e8e2 100644
--- a/src/stores/mission.js
+++ b/src/stores/mission.js
@@ -6,7 +6,7 @@
 // 璧拌埅浠诲姟
 export const useMissionStore = defineStore('mission', () => {
   const missionList = ref([]);
-  const { loading, fetchData } = useFetchData();
+  const { loading, fetchData } = useFetchData(1000);
 
   function fetchMission(type) {
     return fetchData((page, pageSize) => {
diff --git a/src/stores/satellite-grid.js b/src/stores/satellite-grid.js
index 6f73012..526ac9a 100644
--- a/src/stores/satellite-grid.js
+++ b/src/stores/satellite-grid.js
@@ -11,9 +11,10 @@
   const allGridDataList = ref([]);
   // 鍘熷缃戞牸鏁版嵁缁�
   const gridDataList = computed(() => {
-    return allGridDataList.value.filter((v) => {
-      return v.type == 0;
-    });
+    // return allGridDataList.value.filter((v) => {
+    //   return v.type == 0;
+    // });
+    return allGridDataList.value;
   });
   // 铻嶅悎缃戞牸鏁版嵁缁�
   const mixGridDataList = computed(() => {
diff --git a/src/utils/map/grid.js b/src/utils/map/grid.js
index 7f37bf6..49b87d9 100644
--- a/src/utils/map/grid.js
+++ b/src/utils/map/grid.js
@@ -193,47 +193,61 @@
 
   /**
    * 缁樺埗涓�缁勫杈瑰舰
-   * @param {*} points
+   * @param {Array} points 缃戞牸鍧愭爣鐐规暟缁�
+   * @param {Boolean} draw 鏄惁鍒涘缓瀹屾垚鍚庡悓鏃剁粯鍒�
    */
-  drawPolylines(points) {
+  drawPolylines({ points, draw, event }) {
     const gridViews = [];
     points.forEach((p) => {
-      let path = p.map((v) => {
-        // eslint-disable-next-line no-undef
-        return new AMap.LngLat(v[0], v[1]);
-      });
       //鍒涘缓澶氳竟褰� Polygon 瀹炰緥
       // eslint-disable-next-line no-undef
       var polygon = new AMap.Polygon({
-        path: path, //璺緞
+        path: p.path, //璺緞
         fillColor: '#fff', //澶氳竟褰㈠~鍏呴鑹�
         strokeWeight: 1, //绾挎潯瀹藉害锛岄粯璁や负 2
         strokeColor: 'white', //绾挎潯棰滆壊
-        fillOpacity: 0
+        fillOpacity: 0,
+        extData: p.extData
       });
+
+      if (typeof event === 'function') {
+        event(polygon);
+      }
       gridViews.push(polygon);
     });
-    map.add(gridViews);
-    map.setFitView(gridViews);
+    if (draw) {
+      map.add(gridViews);
+      map.setFitView(gridViews);
+    }
     return gridViews;
   },
 
   drawGridText(points, textViews, anchor, type) {
+    // if (textViews) {
+    //   points.forEach((p, i) => {
+    //     textViews[i].setPosition(p.lnglat_GD);
+    //     textViews[i].setText(p.data);
+    //   });
+    //   return textViews;
+    // } else {
+    //   const _textViews = [];
+    //   points.forEach((p) => {
+    //     const m = textMaker(p.lnglat_GD, p.data, anchor, type);
+    //     _textViews.push(m);
+    //   });
+    //   map.add(_textViews);
+    //   return { textViews: _textViews };
+    // }
     if (textViews) {
-      points.forEach((p, i) => {
-        textViews[i].setPosition(p.lnglat_GD);
-        textViews[i].setText(p.data);
-      });
-      return textViews;
-    } else {
-      const _textViews = [];
-      points.forEach((p) => {
-        const m = textMaker(p.lnglat_GD, p.data, anchor, type);
-        _textViews.push(m);
-      });
-      map.add(_textViews);
-      return { textViews: _textViews };
+      map.remove(textViews);
     }
+    const _textViews = [];
+    points.forEach((p) => {
+      const m = textMaker(p.lnglat_GD, p.data, anchor, type);
+      _textViews.push(m);
+    });
+    // map.add(_textViews);
+    return { textViews: _textViews };
   },
 
   drawGridTextLabel(points, textViews, labelsLayer, direction) {
@@ -271,51 +285,111 @@
     }
   },
 
-  drawGridColor(gridViews, texts, factorName) {
-    gridViews.forEach((g, i) => {
-      const data = parseFloat(texts[i]);
-      const { color, nextColor, range, nextRange } =
-        Legend.getStandardColorAndNext(factorName, data);
-      const ratio = (data - range) / (nextRange - range);
-      const _color = getColorBetweenTwoColors(
-        color.map((v) => v * 255),
-        nextColor.map((v) => v * 255),
-        ratio
-      );
-      g.setOptions({
-        fillColor: _color,
-        fillOpacity: color[3] == 0 ? 0 : 0.7
-      });
+  /**
+   * 鏍规嵁閬ユ祴鏁版嵁锛岃缃搴旂綉鏍肩殑鏍囧噯鑹诧紝杩斿洖鏈夋暟鎹殑缃戞牸
+   * @param {Array} gridViews 缃戞牸澶氳竟褰㈠璞℃暟缁�
+   * @param {Array} gridDataDetail 鍗槦閬ユ祴鏁版嵁鏁扮粍
+   * @param {string} factorName 鐩戞祴鍥犲瓙鍚嶇О
+   * @param {number} opacity 閫忔槑搴�
+   */
+  drawGridColor(gridViews, gridDataDetail, factorName, opacity, zIndex) {
+    const res = [];
+    // 閬嶅巻鍗槦閬ユ祴鏁版嵁鏁扮粍
+    gridDataDetail.forEach((d, i) => {
+      if (d.pm25) {
+        const grid = gridViews[i];
+
+        // 鏍规嵁閬ユ祴鏁版嵁璁$畻缃戞牸棰滆壊
+        const data = d.pm25;
+        const { color, nextColor, range, nextRange } =
+          Legend.getStandardColorAndNext(factorName, data);
+        const ratio = (data - range) / (nextRange - range);
+        const _color = getColorBetweenTwoColors(
+          color.map((v) => v * 255),
+          nextColor.map((v) => v * 255),
+          ratio
+        );
+        grid.setOptions({
+          zIndex: zIndex ? zIndex : 10,
+          fillColor: _color,
+          fillOpacity: opacity ? opacity : color[3] == 0 ? 0 : 0.7
+        });
+
+        res.push(grid);
+      }
     });
+
+    return res;
+    // gridViews.forEach((g, i) => {
+    //   const data = parseFloat(gridDataDetail[i]);
+    //   const { color, nextColor, range, nextRange } =
+    //     Legend.getStandardColorAndNext(factorName, data);
+    //   const ratio = (data - range) / (nextRange - range);
+    //   const _color = getColorBetweenTwoColors(
+    //     color.map((v) => v * 255),
+    //     nextColor.map((v) => v * 255),
+    //     ratio
+    //   );
+    //   g.setOptions({
+    //     fillColor: _color,
+    //     fillOpacity: color[3] == 0 ? 0 : 0.7
+    //   });
+    // });
   },
 
-  drawGridColorCustom(gridViews, texts) {
+  drawGridColorCustom(gridViews, gridDataDetail, opacity) {
     var max, min;
-    texts.forEach((t) => {
-      if (!t) return;
-      if (!max || t > max) {
-        max = t;
+    gridDataDetail.forEach((t) => {
+      if (!t.pm25) return;
+      if (!max || t.pm25 > max) {
+        max = t.pm25;
       }
-      if (!min || t < min) {
-        min = t;
+      if (!min || t.pm25 < min) {
+        min = t.pm25;
       }
     });
-    gridViews.forEach((g, i) => {
-      const data = parseFloat(texts[i]);
-      const { color, nextColor, range, nextRange } =
-        Legend.getCustomColorAndNext(data, min, max);
-      const ratio = (data - range) / (nextRange - range);
+    const res = [];
+    // 閬嶅巻鍗槦閬ユ祴鏁版嵁鏁扮粍
+    gridDataDetail.forEach((d, i) => {
+      if (d.pm25) {
+        const grid = gridViews[i];
 
-      const _color = getColorBetweenTwoColors(
-        color.map((v) => v * 255),
-        nextColor.map((v) => v * 255),
-        ratio
-      );
-      g.setOptions({
-        fillColor: _color,
-        // fillOpacity: color[3]
-        fillOpacity: color[3] == 0 ? 0 : 0.7
-      });
+        // 鏍规嵁閬ユ祴鏁版嵁璁$畻缃戞牸棰滆壊
+        const data = d.pm25;
+        const { color, nextColor, range, nextRange } =
+          Legend.getCustomColorAndNext(data, min, max);
+        const ratio = (data - range) / (nextRange - range);
+
+        const _color = getColorBetweenTwoColors(
+          color.map((v) => v * 255),
+          nextColor.map((v) => v * 255),
+          ratio
+        );
+        grid.setOptions({
+          fillColor: _color,
+          fillOpacity: opacity ? opacity : color[3] == 0 ? 0 : 0.7
+        });
+
+        res.push(grid);
+      }
     });
+
+    return res;
+    // gridViews.forEach((g, i) => {
+    //   const data = parseFloat(gridDataDetail[i]);
+    //   const { color, nextColor, range, nextRange } =
+    //     Legend.getCustomColorAndNext(data, min, max);
+    //   const ratio = (data - range) / (nextRange - range);
+
+    //   const _color = getColorBetweenTwoColors(
+    //     color.map((v) => v * 255),
+    //     nextColor.map((v) => v * 255),
+    //     ratio
+    //   );
+    //   g.setOptions({
+    //     fillColor: _color,
+    //     fillOpacity: color[3] == 0 ? 0 : 0.7
+    //   });
+    // });
   }
 };
diff --git a/src/utils/map/line.js b/src/utils/map/line.js
new file mode 100644
index 0000000..57869ac
--- /dev/null
+++ b/src/utils/map/line.js
@@ -0,0 +1,98 @@
+import { map } from './index_old';
+import calculate from './calculate';
+import { getHexColor } from '../color';
+
+var _polylineArr = [];
+
+function newPolyline(path, color) {
+  // eslint-disable-next-line no-undef
+  return new AMap.Polyline({
+    path: path,
+    strokeStyle: 'solid',
+    isOutline: true,
+    borderWeight: 1,
+    outlineColor: 'white',
+    strokeWeight: 4, // 绾挎潯瀹藉害锛岄粯璁や负 1
+    strokeColor: color, // 绾挎潯棰滆壊
+    lineJoin: 'round', // 鎶樼嚎鎷愮偣杩炴帴澶勬牱寮�
+    showDir: true
+  });
+}
+
+export default {
+  drawLine(fDatas, factor) {
+    const lnglats_GD = fDatas.lnglats_GD;
+    const colors = factor.colors;
+
+    this.hideLine();
+
+    var path = calculate.parse2LngLat(lnglats_GD);
+
+    let sIndex = 0;
+    for (let i = 1; i < path.length; i++) {
+      var r = lnglats_GD[i];
+      var lastP = lnglats_GD[i - 1];
+      // eslint-disable-next-line no-undef
+      var distance = AMap.GeometryUtil.distance(r, lastP);
+      const c = colors[i];
+      const lastC = colors[i - 1];
+      if (distance > 500 || c != lastC) {
+        let _path, _color;
+        // 褰撲袱鐐硅窛绂昏秴杩�500鏃讹紝璁や负涓ょ偣涓嶈繛缁紝涓嶇粯鍒惰繛绾�
+        if (distance > 500) {
+          _path = path.slice(sIndex, i);
+          _color = getHexColor(
+            lastC.map((v, index) => {
+              if (index < lastC.length - 1) {
+                return v * 255;
+              } else {
+                return v;
+              }
+            })
+          );
+        } else {
+          _path = path.slice(sIndex, i + 1);
+          _color = getHexColor(
+            c.map((v, index) => {
+              if (index < c.length - 1) {
+                return v * 255;
+              } else {
+                return v;
+              }
+            })
+          );
+        }
+
+        // 鍒涘缓鎶樼嚎瀹炰緥
+        const polyline = newPolyline(_path, _color);
+        _polylineArr.push(polyline);
+
+        sIndex = i;
+      }
+    }
+    if (sIndex == 0) {
+      const c = colors[sIndex];
+      const _color = getHexColor(
+        c.map((v, index) => {
+          if (index < c.length - 1) {
+            return v * 255;
+          } else {
+            return v;
+          }
+        })
+      );
+      const polyline = newPolyline(path, _color);
+      _polylineArr.push(polyline);
+    }
+    // 灏嗘姌绾挎坊鍔犺嚦鍦板浘瀹炰緥
+    map.add(_polylineArr);
+    return _polylineArr;
+  },
+
+  hideLine() {
+    if (_polylineArr) {
+      map.remove(_polylineArr);
+      _polylineArr = [];
+    }
+  }
+};
diff --git a/src/views/HomePage.vue b/src/views/HomePage.vue
index 99df365..6509893 100644
--- a/src/views/HomePage.vue
+++ b/src/views/HomePage.vue
@@ -10,8 +10,18 @@
       <!-- <MapLocation></MapLocation> -->
       <SceneSearch></SceneSearch>
       <MapScene></MapScene>
-      <el-button @click="satelliteImportVisible = !satelliteImportVisible" type="primary" class="el-button-custom satellite-right-top p-events-auto">鐩戞祴鏁版嵁瀵煎叆</el-button>
-      <el-button @click="aodImportVisible = !aodImportVisible" type="primary" class="el-button-custom aod-right-top p-events-auto">AOD鏁版嵁瀵煎叆</el-button>
+      <el-button
+        @click="satelliteImportVisible = !satelliteImportVisible"
+        type="primary"
+        class="el-button-custom satellite-right-top p-events-auto"
+        >鐩戞祴鏁版嵁瀵煎叆</el-button
+      >
+      <el-button
+        @click="aodImportVisible = !aodImportVisible"
+        type="primary"
+        class="el-button-custom aod-right-top p-events-auto"
+        >AOD鏁版嵁瀵煎叆</el-button
+      >
     </el-row>
     <!-- <CoreMenu></CoreMenu> -->
     <router-view></router-view>
@@ -25,8 +35,8 @@
 import { ref } from 'vue';
 import SatelliteImport from './satellitetelemetry/component/SatelliteImport.vue';
 import AODImport from './satellitetelemetry/component/AODImport.vue';
-const satelliteImportVisible = ref(false)
-const aodImportVisible = ref(false)
+const satelliteImportVisible = ref(false);
+const aodImportVisible = ref(false);
 </script>
 
 <style scoped>
diff --git a/src/views/satellitetelemetry/SatelliteProxy.js b/src/views/satellitetelemetry/SatelliteProxy.js
index 7b7f8cf..03973ea 100644
--- a/src/views/satellitetelemetry/SatelliteProxy.js
+++ b/src/views/satellitetelemetry/SatelliteProxy.js
@@ -1,6 +1,43 @@
 import calculate from '@/utils/map/calculate';
 import gridMapUtil from '@/utils/map/grid';
-import { map } from '@/utils/map/index_old';
+import { map, onMapMounted } from '@/utils/map/index_old';
+
+let districtPolygon;
+// 缁樺埗鍖哄幙杈圭晫
+function drawDistrict(districtName, isNew) {
+  onMapMounted(() => {
+    if (districtPolygon && !isNew) {
+      map.remove(districtPolygon);
+      map.add(districtPolygon);
+    } else {
+      // eslint-disable-next-line no-undef
+      var district = new AMap.DistrictSearch({
+        extensions: 'all', //杩斿洖琛屾斂鍖鸿竟鐣屽潗鏍囩瓑鍏蜂綋淇℃伅
+        level: 'district' //璁剧疆鏌ヨ琛屾斂鍖虹骇鍒负鍖�
+      });
+      district.search(districtName, function (status, result) {
+        var bounds = result.districtList[0].boundaries; //鑾峰彇鏈濋槼鍖虹殑杈圭晫淇℃伅
+        if (bounds) {
+          for (var i = 0; i < bounds.length; i++) {
+            //鐢熸垚琛屾斂鍖哄垝 polygon
+            // eslint-disable-next-line no-undef
+            districtPolygon = new AMap.Polygon({
+              map: map, //鏄剧ず璇ヨ鐩栫墿鐨勫湴鍥惧璞�
+              strokeWeight: 2, //杞粨绾垮搴�
+              path: bounds[i], //澶氳竟褰㈣疆寤撶嚎鐨勮妭鐐瑰潗鏍囨暟缁�
+              fillOpacity: 0, //澶氳竟褰㈠~鍏呴�忔槑搴�
+              fillColor: '#CCF3FF', //澶氳竟褰㈠~鍏呴鑹�
+              // strokeColor: '#ffffff' //绾挎潯棰滆壊
+              strokeColor: '#0552f7', //绾挎潯棰滆壊
+              zIndex: 9
+            });
+          }
+          map.setFitView(); //灏嗚鐩栫墿璋冩暣鍒板悎閫傝閲�
+        }
+      });
+    }
+  });
+}
 
 function clearAll(mapViews) {
   if (mapViews) {
@@ -20,34 +57,39 @@
 }
 
 // 缁樺埗缃戞牸绾�
-function drawPolyline(gridInfo) {
+function drawPolyline(gridInfo, event) {
   // 缁樺埗缃戞牸
   const points = gridInfo.map((v) => {
-    // return calculate.wgs84_To_Gcj02(v.longitude, v.latitude);
-    return [v.longitude, v.latitude];
+    return calculate.wgs84_To_Gcj02(v.longitude, v.latitude);
+    // return [v.longitude, v.latitude];
   });
   // const gridPoints = gridMapUtil.parseGridPoint(points);
   // console.log('gridPoints:', gridPoints);
 
-  const gridPoints = gridInfo.map((v) => {
-    return [
-      calculate.wgs84_To_Gcj02(v.point1Lon, v.point1Lat),
-      calculate.wgs84_To_Gcj02(v.point2Lon, v.point2Lat),
-      calculate.wgs84_To_Gcj02(v.point3Lon, v.point3Lat),
-      calculate.wgs84_To_Gcj02(v.point4Lon, v.point4Lat)
-      // [v.point1Lon, v.point1Lat],
-      // [v.point2Lon, v.point2Lat],
-      // [v.point3Lon, v.point3Lat],
-      // [v.point4Lon, v.point4Lat]
-    ];
+  const gridPoints = gridInfo.map((v, i) => {
+    return {
+      path: [
+        calculate.wgs84_To_Gcj02(v.point1Lon, v.point1Lat),
+        calculate.wgs84_To_Gcj02(v.point2Lon, v.point2Lat),
+        calculate.wgs84_To_Gcj02(v.point3Lon, v.point3Lat),
+        calculate.wgs84_To_Gcj02(v.point4Lon, v.point4Lat)
+        // [v.point1Lon, v.point1Lat],
+        // [v.point2Lon, v.point2Lat],
+        // [v.point3Lon, v.point3Lat],
+        // [v.point4Lon, v.point4Lat]
+      ]
+        // eslint-disable-next-line no-undef
+        .map((d) => new AMap.LngLat(d[0], d[1])),
+      extData: points[i]
+    };
   });
-  const gridViews = gridMapUtil.drawPolylines(gridPoints);
+  const gridViews = gridMapUtil.drawPolylines({ points: gridPoints, event });
   return { gridViews, gridPoints, points };
 }
 
 // 缁樺埗鐩戞祴鏁版嵁鍊�
-function drawDataText(points, gridData, textViews, labelsLayer) {
-  const data = gridData.map((v, i) => {
+function drawDataText(points, gridDataDetail, textViews, labelsLayer) {
+  const data = gridDataDetail.map((v, i) => {
     return {
       lnglat_GD: points[i],
       // data: v.pm25 ? (v.pm25 + '渭g/m鲁') : ''
@@ -59,8 +101,8 @@
 }
 
 // 缁樺埗鐩戞祴鏁版嵁鎺掑悕鏂囨湰
-function drawRankText(points, gridData, textViews, labelsLayer) {
-  const data = gridData.map((v, i) => {
+function drawRankText(points, gridDataDetail, textViews, labelsLayer) {
+  const data = gridDataDetail.map((v, i) => {
     return {
       lnglat_GD: points[i],
       // data: v.pm25 ? ('鎺掑悕: ' + v.rank) : ''
@@ -72,18 +114,61 @@
 }
 
 // 缁樺埗鐩戞祴鏁版嵁鍊煎搴旂綉鏍奸鑹�
-function drawColor(gridViews, gridData, customColor) {
-  const pm25Data = gridData.map((v) => {
-    return v.pm25;
+function drawColor({
+  gridViews,
+  points,
+  gridDataDetail,
+  lastGridViews,
+  opacity,
+  zIndex,
+  customColor
+}) {
+  // 鏍规嵁鏁版嵁绛涢�夋湁鏁版嵁鐨勭綉鏍�
+  const res = [];
+  // 浠ュ強瀵瑰簲鐨勪腑蹇冪偣鍧愭爣
+  const pointsRes = [];
+  gridDataDetail.forEach((d) => {
+    // 鏍规嵁鏁版嵁鍏宠仈鐨勭綉鏍肩紪鍙凤紝鎵惧埌瀵瑰簲缃戞牸
+    const cellId = d.cellId;
+    if (cellId > gridViews.length) {
+      throw Error(
+        '閬ユ祴鏁版嵁鐨勭綉鏍肩储寮曠紪鍙疯秴鍑虹綉鏍肩粍鑼冨洿锛屾暟鎹拰缃戞牸缁勫彲鑳戒笉瀵瑰簲'
+      );
+    }
+    res.push(gridViews[cellId - 1]);
+    pointsRes.push(points[cellId - 1]);
   });
+
+  // 鏍规嵁缁樺埗棰滆壊鏂瑰紡缁樺埗缃戞牸
+  let resGridViews;
   if (customColor) {
-    gridMapUtil.drawGridColorCustom(gridViews, pm25Data);
+    resGridViews = gridMapUtil.drawGridColorCustom(
+      res,
+      gridDataDetail,
+      opacity,
+      zIndex
+    );
   } else {
-    gridMapUtil.drawGridColor(gridViews, pm25Data, 'PM25');
+    resGridViews = gridMapUtil.drawGridColor(
+      res,
+      gridDataDetail,
+      'PM25',
+      opacity,
+      zIndex
+    );
   }
+
+  if (lastGridViews) {
+    map.remove(lastGridViews);
+  }
+  map.add(resGridViews);
+  map.setFitView(resGridViews);
+
+  return { resGridViews, pointsRes };
 }
 
 export default {
+  drawDistrict,
   drawPolyline,
   drawDataText,
   drawRankText,
diff --git a/src/views/satellitetelemetry/SatelliteTelemetry copy.vue b/src/views/satellitetelemetry/SatelliteTelemetry copy.vue
index 2c516aa..e1020f4 100644
--- a/src/views/satellitetelemetry/SatelliteTelemetry copy.vue
+++ b/src/views/satellitetelemetry/SatelliteTelemetry copy.vue
@@ -122,7 +122,10 @@
   );
   mapViews.textViews = textViews;
   mapViews.labelsLayer = labelsLayer;
-  SatelliteProxy.drawColor(mapViews.gridViews, gridData);
+  SatelliteProxy.drawColor({
+    gridViews: mapViews.gridViews,
+    gridDataDetail: gridData
+  });
 }
 
 function handleRowClick(row) {
diff --git a/src/views/satellitetelemetry/SatelliteTelemetry.vue b/src/views/satellitetelemetry/SatelliteTelemetry.vue
index 66ed9cc..d268ece 100644
--- a/src/views/satellitetelemetry/SatelliteTelemetry.vue
+++ b/src/views/satellitetelemetry/SatelliteTelemetry.vue
@@ -12,6 +12,7 @@
           @show-rank="handleRankClick"
           @show-data="handleDataClick"
           @change-color="handleColorClick"
+          @change-opacity="handleOpacityClick"
         ></SatelliteManage>
       </el-row>
     </el-col>
@@ -27,6 +28,8 @@
   </el-row>
   <SatelliteDataMix class="data-mix" @mix-data="handleMixDataClick">
   </SatelliteDataMix>
+  <SatelliteMixTool :group-id="3"></SatelliteMixTool>
+
   <!-- <el-row class="historical" justify="center">
     <SatelliteAnimation
       :loading="animaLoading"
@@ -45,42 +48,15 @@
 import gridApi from '@/api/gridApi';
 import SatelliteManage from './component/SatelliteManage.vue';
 import SatelliteDataMix from './component/SatelliteDataMix.vue';
+import SatelliteMixTool from './component/SatelliteMixTool.vue';
 import SatelliteProxy from './SatelliteProxy';
 import { useFetchData } from '@/composables/fetchData';
 import { useSatelliteGridStore } from '@/stores/satellite-grid';
-
-// 鏌ヨ闀垮畞鍖鸿鏀垮尯鍒�
-function initDistrict() {
-  onMapMounted(() => {
-    // eslint-disable-next-line no-undef
-    var district = new AMap.DistrictSearch({
-      extensions: 'all', //杩斿洖琛屾斂鍖鸿竟鐣屽潗鏍囩瓑鍏蜂綋淇℃伅
-      level: 'district' //璁剧疆鏌ヨ琛屾斂鍖虹骇鍒负鍖�
-    });
-    district.search('闀垮畞鍖�', function (status, result) {
-      var bounds = result.districtList[0].boundaries; //鑾峰彇鏈濋槼鍖虹殑杈圭晫淇℃伅
-      if (bounds) {
-        for (var i = 0; i < bounds.length; i++) {
-          //鐢熸垚琛屾斂鍖哄垝 polygon
-          // eslint-disable-next-line no-undef
-          var polygon = new AMap.Polygon({
-            map: map, //鏄剧ず璇ヨ鐩栫墿鐨勫湴鍥惧璞�
-            strokeWeight: 3, //杞粨绾垮搴�
-            path: bounds[i], //澶氳竟褰㈣疆寤撶嚎鐨勮妭鐐瑰潗鏍囨暟缁�
-            fillOpacity: 0.1, //澶氳竟褰㈠~鍏呴�忔槑搴�
-            fillColor: '#CCF3FF', //澶氳竟褰㈠~鍏呴鑹�
-            // strokeColor: '#ffffff' //绾挎潯棰滆壊
-            strokeColor: '#0077ff' //绾挎潯棰滆壊
-          });
-        }
-        map.setFitView(); //灏嗚鐩栫墿璋冩暣鍒板悎閫傝閲�
-      }
-    });
-  });
-}
+import { useSceneStore } from '@/stores/scene';
 
 const satelliteGridStore = useSatelliteGridStore();
 const { loading, fetchData } = useFetchData(10000);
+const sceneStore = useSceneStore();
 const animaLoading = ref(true);
 const show = ref(true);
 
@@ -96,7 +72,8 @@
 function onSearch(options) {
   satelliteGridStore.fetchGridCell(options.id).then(() => {
     drawGrid(satelliteGridStore.gridInfo);
-    initDistrict();
+    // initDistrict();
+    SatelliteProxy.drawDistrict('闀垮畞鍖�');
   });
   satelliteGridStore.fetchGridData(options.id).then(() => {
     max = satelliteGridStore.gridDataList.length;
@@ -133,16 +110,46 @@
 
 function drawGrid(gridInfo) {
   SatelliteProxy.clearAll(mapViews);
-  mapViews = SatelliteProxy.drawPolyline(gridInfo);
+  mapViews = SatelliteProxy.drawPolyline(gridInfo, (polygon) => {
+    //榧犳爣绉诲叆浜嬩欢
+    polygon.on('mouseover', () => {
+      polygon.setOptions({
+        //淇敼澶氳竟褰㈠睘鎬х殑鏂规硶
+        strokeWeight: 2,
+        strokeColor: 'red'
+      });
+    });
+    //榧犳爣绉诲嚭浜嬩欢
+    polygon.on('mouseout', () => {
+      polygon.setOptions({
+        strokeWeight: 1,
+        strokeColor: 'white'
+      });
+    });
+    //榧犳爣鐐瑰嚮浜嬩欢
+    polygon.on('click', () => {
+      const [lng, lat] = polygon.getExtData();
+      sceneStore.radius = 0.5;
+      sceneStore.searchScene(lng, lat);
+    });
+  });
 }
 
 // 缁樺埗缃戞牸閬ユ劅鏁版嵁鍊煎拰缃戞牸棰滆壊
 function drawTextAndColor(gridData) {
   // SatelliteProxy.clearText(mapViews);
+  const { resGridViews, pointsRes } = SatelliteProxy.drawColor({
+    gridViews: mapViews.gridViews,
+    points: mapViews.points,
+    gridDataDetail: gridData,
+    lastGridViews: mapViews.lastGridViews
+  });
+  mapViews.lastGridViews = resGridViews;
+  mapViews.lastPoints = pointsRes;
   // 鏂囨湰鏍囪
   const { textViews: dataTxt, labelsLayer: dataLayer } =
     SatelliteProxy.drawDataText(
-      mapViews.points,
+      mapViews.lastPoints,
       gridData,
       mapViews.dataTxt,
       mapViews.dataLayer
@@ -151,14 +158,13 @@
   mapViews.dataLayer = dataLayer;
   const { textViews: rankTxt, labelsLayer: rankLayer } =
     SatelliteProxy.drawRankText(
-      mapViews.points,
+      mapViews.lastPoints,
       gridData,
       mapViews.rankTxt,
       mapViews.rankLayer
     );
   mapViews.rankTxt = rankTxt;
   mapViews.rankLayer = rankLayer;
-  SatelliteProxy.drawColor(mapViews.gridViews, gridData);
 }
 
 let selectedGridData;
@@ -188,11 +194,23 @@
 }
 
 function handleColorClick(isStandardColor) {
-  SatelliteProxy.drawColor(
-    mapViews.gridViews,
-    selectedGridData,
-    !isStandardColor
-  );
+  const { resGridViews, pointsRes } = SatelliteProxy.drawColor({
+    gridViews: mapViews.gridViews,
+    points: mapViews.points,
+    gridDataDetail: selectedGridData,
+    lastGridViews: mapViews.lastGridViews,
+    customColor: !isStandardColor
+  });
+  mapViews.lastGridViews = resGridViews;
+  mapViews.lastPoints = pointsRes;
+}
+
+function handleOpacityClick(isOpacity) {
+  mapViews.lastGridViews.forEach((e) => {
+    e.setOptions({
+      fillOpacity: isOpacity ? 0.1 : 0.7
+    });
+  });
 }
 
 function handleMixDataClick(gridData) {
@@ -215,5 +233,6 @@
   position: absolute;
   right: 0;
   top: 60px;
+  /* color: #0552f7; */
 }
 </style>
diff --git a/src/views/satellitetelemetry/component/SatelliteDataMix.vue b/src/views/satellitetelemetry/component/SatelliteDataMix.vue
index 8aa13eb..b19b865 100644
--- a/src/views/satellitetelemetry/component/SatelliteDataMix.vue
+++ b/src/views/satellitetelemetry/component/SatelliteDataMix.vue
@@ -3,7 +3,7 @@
     <el-col span="2">
       <el-row>
         <CardButton
-          name="鏁版嵁铻嶅悎"
+          name="缃戞牸婧簮"
           direction="left"
           @click="() => (show = !show)"
         ></CardButton>
diff --git a/src/views/satellitetelemetry/component/SatelliteManage.vue b/src/views/satellitetelemetry/component/SatelliteManage.vue
index cfa584a..318f624 100644
--- a/src/views/satellitetelemetry/component/SatelliteManage.vue
+++ b/src/views/satellitetelemetry/component/SatelliteManage.vue
@@ -6,30 +6,40 @@
           :loading="loading"
           @search="onSearch"
         ></SatelliteSearchBar>
-        <el-button
-          type="primary"
-          class="el-button-custom"
-          size="small"
-          @click="handleRankClick"
-        >
-          {{ rankVisible ? '闅愯棌鎺掑悕' : '鏄剧ず鎺掑悕' }}
-        </el-button>
-        <el-button
-          type="primary"
-          class="el-button-custom"
-          size="small"
-          @click="handleDataClick"
-        >
-          {{ dataVisible ? '闅愯棌鏁版嵁' : '鏄剧ず鏁版嵁' }}
-        </el-button>
-        <el-button
-          type="primary"
-          class="el-button-custom"
-          size="small"
-          @click="handleColorClick"
-        >
-          {{ isStandardColor ? '缁樺埗瀵规瘮鑹�' : '缁樺埗鏍囧噯鑹�' }}
-        </el-button>
+        <el-row class="m-b-8">
+          <el-button
+            type="primary"
+            class="el-button-custom"
+            size="small"
+            @click="handleRankClick"
+          >
+            {{ rankVisible ? '闅愯棌鎺掑悕' : '鏄剧ず鎺掑悕' }}
+          </el-button>
+          <el-button
+            type="primary"
+            class="el-button-custom"
+            size="small"
+            @click="handleDataClick"
+          >
+            {{ dataVisible ? '闅愯棌鏁版嵁' : '鏄剧ず鏁版嵁' }}
+          </el-button>
+          <el-button
+            type="primary"
+            class="el-button-custom"
+            size="small"
+            @click="handleColorClick"
+          >
+            {{ isStandardColor ? '缁樺埗瀵规瘮鑹�' : '缁樺埗鏍囧噯鑹�' }}
+          </el-button>
+          <el-button
+            type="primary"
+            class="el-button-custom"
+            size="small"
+            @click="handleOpacityClick"
+          >
+            {{ !isOpacity ? '閫忔槑鍖�' : '鍙栨秷閫忔槑鍖�' }}
+          </el-button>
+        </el-row>
         <el-table
           :data="gridDataList"
           table-layout="fixed"
@@ -109,11 +119,18 @@
   }
 });
 
-const rankVisible = ref(true);
-const dataVisible = ref(true);
-const isStandardColor = ref(true)
+const rankVisible = ref(false);
+const dataVisible = ref(false);
+const isStandardColor = ref(true);
+const isOpacity = ref(false);
 
-const emits = defineEmits(['search', 'rowClick', 'showRank', 'showData', 'changeColor']);
+const emits = defineEmits([
+  'search',
+  'rowClick',
+  'showRank',
+  'showData',
+  'changeColor'
+]);
 
 // 鏌ヨ缃戞牸淇℃伅鍜岄仴鎰熸暟鎹粍
 function onSearch(options) {
@@ -121,20 +138,25 @@
 }
 
 function handleRankClick() {
-  rankVisible.value = !rankVisible.value
+  rankVisible.value = !rankVisible.value;
   emits('showRank', rankVisible.value);
 }
 
 function handleDataClick() {
-  dataVisible.value = !dataVisible.value
+  dataVisible.value = !dataVisible.value;
   emits('showData', dataVisible.value);
 }
 
 function handleColorClick() {
-  isStandardColor.value = !isStandardColor.value
+  isStandardColor.value = !isStandardColor.value;
   emits('changeColor', isStandardColor.value);
 }
 
+function handleOpacityClick() {
+  isOpacity.value = !isOpacity.value;
+  emits('changeOpacity', isOpacity.value);
+}
+
 function handleRowClick(row, col, event) {
   emits('rowClick', row);
 }
diff --git a/src/views/satellitetelemetry/component/SatelliteMixTool.vue b/src/views/satellitetelemetry/component/SatelliteMixTool.vue
new file mode 100644
index 0000000..9f10545
--- /dev/null
+++ b/src/views/satellitetelemetry/component/SatelliteMixTool.vue
@@ -0,0 +1,311 @@
+<template>
+  <CardDialog
+    v-model="dialogVisible"
+    title="璧拌埅铻嶅悎"
+    draggable
+    :modal="false"
+    width="400px"
+  >
+    <template #default>
+      <el-row>
+        <OptionMission v-model="mission"></OptionMission>
+        <el-text size="small" :type="fusionData ? 'success' : 'warning'">{{
+          fusionData ? '璧拌埅鏁版嵁宸茶瀺鍚�' : '璧拌埅鏁版嵁鏈瀺鍚�'
+        }}</el-text>
+      </el-row>
+      <el-row>
+        <el-button
+          type="primary"
+          class="el-button-custom"
+          size="small"
+          :disabled="!gridCellList || !fusionData"
+          @click="handleFusionClick"
+        >
+          {{ '鍙犲姞铻嶅悎' }}
+        </el-button>
+        <el-button
+          type="primary"
+          class="el-button-custom"
+          size="small"
+          @click="handleGridClick"
+        >
+          {{ gridVisible ? '闅愯棌铻嶅悎' : '鏄剧ず铻嶅悎' }}
+        </el-button>
+        <el-button
+          type="primary"
+          class="el-button-custom"
+          size="small"
+          :loading="loading"
+          @click="handleUnderwayClick"
+        >
+          {{ underwayVisible ? '闅愯棌璧拌埅璺嚎' : '鏄剧ず璧拌埅璺嚎' }}
+        </el-button>
+      </el-row>
+      <el-row class="m-t-8">
+        <el-button
+          type="primary"
+          class="el-button-custom"
+          size="small"
+          @click="handleRankClick"
+        >
+          {{ rankVisible ? '闅愯棌鎺掑悕' : '鏄剧ず鎺掑悕' }}
+        </el-button>
+        <el-button
+          type="primary"
+          class="el-button-custom"
+          size="small"
+          @click="handleDataClick"
+        >
+          {{ dataVisible ? '闅愯棌鏁版嵁' : '鏄剧ず鏁版嵁' }}
+        </el-button>
+        <el-button
+          type="primary"
+          class="el-button-custom"
+          size="small"
+          @click="handleColorClick"
+        >
+          {{ isStandardColor ? '缁樺埗瀵规瘮鑹�' : '缁樺埗鏍囧噯鑹�' }}
+        </el-button>
+        <el-button
+          type="primary"
+          class="el-button-custom"
+          size="small"
+          @click="handleOpacityClick"
+        >
+          {{ !isOpacity ? '閫忔槑鍖�' : '鍙栨秷閫忔槑鍖�' }}
+        </el-button>
+      </el-row>
+    </template>
+    <template #footer>
+      <el-row justify="start" align="middle" class="p-b-2 one-row">
+        <!-- <el-text size="small" type="warning">鎼滅储鑼冨洿</el-text> -->
+      </el-row>
+    </template>
+  </CardDialog>
+</template>
+<script setup>
+import { ref, onMounted, onUnmounted, watch } from 'vue';
+import moment from 'moment';
+import { map, onMapMounted } from '@/utils/map/index_old';
+import gridApi from '@/api/gridApi';
+import SatelliteProxy from '../SatelliteProxy';
+
+const props = defineProps({
+  groupId: {
+    type: Number,
+    default: 3
+  }
+});
+
+const dialogVisible = ref(true);
+const mission = ref(undefined);
+
+const gridCellList = ref(undefined);
+const fusionData = ref(undefined);
+const gridDataDetailMap = new Map();
+
+const gridVisible = ref(true);
+const underwayVisible = ref(false);
+const rankVisible = ref(false);
+const dataVisible = ref(false);
+const isStandardColor = ref(true);
+const isOpacity = ref(false);
+
+// 鍦板浘缃戞牸鐩稿叧瀵硅薄
+let mapViews;
+
+// 妫�鏌ヨ蛋鑸暟鎹槸鍚﹀拰100绫崇綉鏍煎凡铻嶅悎
+function checkUnderwayFusionResult() {
+  const time = moment(mission.value.startTime).format('YYYY-MM-DD HH:mm:ss');
+  gridApi.fetchGridData(props.groupId, time, 3).then((res) => {
+    if (res.data.length > 0) {
+      fusionData.value = res.data[0];
+    } else {
+      fusionData.value = undefined;
+    }
+  });
+}
+
+function drawGrid(gridInfo) {
+  SatelliteProxy.clearAll(mapViews);
+  mapViews = SatelliteProxy.drawPolyline(gridInfo);
+}
+
+// 缁樺埗缃戞牸閬ユ劅鏁版嵁鍊煎拰缃戞牸棰滆壊
+function drawTextAndColor(gridData) {
+  const { resGridViews, pointsRes } = SatelliteProxy.drawColor({
+    gridViews: mapViews.gridViews,
+    points: mapViews.points,
+    gridDataDetail: gridData,
+    lastGridViews: mapViews.lastGridViews,
+    opacity: 1,
+    zIndex: 11
+  });
+  mapViews.lastGridViews = resGridViews;
+  mapViews.lastPoints = pointsRes;
+  // 鏂囨湰鏍囪
+  const { textViews: dataTxt, labelsLayer: dataLayer } =
+    SatelliteProxy.drawDataText(
+      mapViews.lastPoints,
+      gridData,
+      mapViews.dataTxt,
+      mapViews.dataLayer
+    );
+  mapViews.dataTxt = dataTxt;
+  mapViews.dataLayer = dataLayer;
+  const { textViews: rankTxt, labelsLayer: rankLayer } =
+    SatelliteProxy.drawRankText(
+      mapViews.lastPoints,
+      gridData,
+      mapViews.rankTxt,
+      mapViews.rankLayer
+    );
+  mapViews.rankTxt = rankTxt;
+  mapViews.rankLayer = rankLayer;
+}
+
+watch(mission, (nV, oV) => {
+  if (nV != oV) {
+    checkUnderwayFusionResult();
+    search(nV);
+  }
+});
+
+watch(
+  () => props.groupId,
+  (nV, oV) => {
+    if (nV != oV) {
+      gridApi.fetchGridCell(nV).then((res) => {
+        gridCellList.value = res.data;
+        drawGrid(gridCellList.value);
+      });
+    }
+  },
+  {
+    immediate: true
+  }
+);
+
+let selectedGridData;
+// 鍙犲姞铻嶅悎
+function handleFusionClick() {
+  const d = fusionData.value;
+  if (gridDataDetailMap.has(d.id)) {
+    const gridData = gridDataDetailMap.get(d.id);
+    selectedGridData = gridData;
+    drawTextAndColor(gridData);
+  } else {
+    gridApi.fetchGridDataDetail(d.id, d.groupId).then((res) => {
+      gridDataDetailMap.set(d.id, res.data);
+      const gridData = res.data;
+      selectedGridData = gridData;
+      drawTextAndColor(gridData);
+    });
+  }
+}
+
+function handleGridClick() {
+  gridVisible.value = !gridVisible.value;
+  gridVisible.value
+    ? map.add(mapViews.lastGridViews)
+    : map.remove(mapViews.lastGridViews);
+}
+
+function handleUnderwayClick() {
+  underwayVisible.value = !underwayVisible.value;
+  underwayVisible.value ? draw() : mapLine.hideLine();
+}
+
+function handleRankClick() {
+  rankVisible.value = !rankVisible.value;
+  rankVisible.value ? map.add(mapViews.rankTxt) : map.remove(mapViews.rankTxt);
+}
+
+function handleDataClick() {
+  dataVisible.value = !dataVisible.value;
+  dataVisible.value ? map.add(mapViews.dataTxt) : map.remove(mapViews.dataTxt);
+}
+
+function handleColorClick() {
+  isStandardColor.value = !isStandardColor.value;
+  const { resGridViews, pointsRes } = SatelliteProxy.drawColor({
+    gridViews: mapViews.gridViews,
+    points: mapViews.points,
+    gridDataDetail: selectedGridData,
+    lastGridViews: mapViews.lastGridViews,
+    customColor: !isStandardColor.value,
+    opacity: 1,
+    zIndex: 11
+  });
+  mapViews.lastGridViews = resGridViews;
+  mapViews.lastPoints = pointsRes;
+}
+
+function handleOpacityClick() {
+  isOpacity.value = !isOpacity.value;
+  mapViews.lastGridViews.forEach((e) => {
+    e.setOptions({
+      fillOpacity: isOpacity.value ? 0.1 : 1
+    });
+  });
+}
+
+/**璧拌埅杞ㄨ抗*******************************************************************/
+import { FactorDatas } from '@/model/FactorDatas';
+import sector from '@/utils/map/sector';
+import mapLine from '@/utils/map/line';
+import mapUtil from '@/utils/map/util';
+import { fetchHistoryData } from '@/utils/factor/data';
+import { useFetchData } from '@/composables/fetchData';
+
+onMounted(() => (isUnmounted.value = false));
+onUnmounted(() => {
+  mapUtil.clearMap();
+  isUnmounted.value = true;
+});
+
+const { loading, fetchData } = useFetchData(10000);
+
+const isUnmounted = ref(false);
+const deviceType = ref(undefined);
+const drawMode = ref(0);
+// 鐩戞祴鏁版嵁
+const factorDatas = ref(new FactorDatas());
+// pm2.5
+const factorType = 6;
+
+function onFetchData(dType, data) {
+  if (isUnmounted.value) return;
+  // todo 鏍规嵁璁惧绫诲瀷鍒囨崲鍦板浘鐩戞祴鍥犲瓙灞曠ず鍗曢�夋銆佹姌绾垮浘澶嶉�夋銆佹暟鎹〃鏍煎閫夋鐨勫洜瀛愮被鍨�
+  deviceType.value = dType;
+  factorDatas.value.setData(data, drawMode.value, () => {
+    factorDatas.value.refreshHeight(factorType.value);
+  });
+}
+
+function search(option) {
+  const { deviceType, deviceCode, startTime, endTime } = option;
+  // deviceType.value = deviceType;
+  // deviceCode.value = deviceCode;
+  const _startTime = moment(startTime).format('YYYY-MM-DD HH:mm:ss');
+  const _endTime = moment(endTime).format('YYYY-MM-DD HH:mm:ss');
+  fetchData((page, pageSize) => {
+    return fetchHistoryData({
+      deviceType,
+      deviceCode,
+      startTime: _startTime,
+      endTime: _endTime,
+      page,
+      perPage: pageSize
+    }).then((res) => onFetchData(deviceType, res.data));
+  });
+}
+
+function draw() {
+  // 鍒锋柊鍥句緥
+  const factor = factorDatas.value.factor[factorType];
+  sector.clearSector();
+  factorDatas.value.refreshHeight(factorType);
+  mapLine.drawLine(factorDatas.value, factor);
+}
+</script>

--
Gitblit v1.9.3