Browse Source

辖区要素

CzRger 1 year ago
parent
commit
612f1c51b3

+ 4 - 4
src/router/index.ts

@@ -20,13 +20,13 @@ const routes = [
     },
     ...shipTestRouter,
     {
-        path: '/ship-playback',
-        component: () => import('@/views/ship-playback/index.vue'),
-    },
-    {
         path: '/element',
         component: () => import('@/views/element/index-ol.vue'),
     },
+    {
+        path: '/ship-playback',
+        component: () => import('@/views/ship-playback/index.vue'),
+    },
 ]
 
 const router = createRouter({

+ 71 - 0
src/views/element/elementStyle.ts

@@ -0,0 +1,71 @@
+import * as ol from 'ol'
+import * as style from 'ol/style'
+import * as layer from 'ol/layer'
+import * as source from 'ol/source'
+import * as geom from 'ol/geom'
+import * as proj from 'ol/proj'
+import * as interaction from 'ol/interaction'
+import * as extent from "ol/extent";
+import RadarXMB from './img/element-radar-xmb.svg'
+import * as format from "ol/format";
+import {Fill, Stroke} from "ol/style";
+const radarPoint = () => {
+  let _style: any = []
+  _style.push(new style.Style({
+    image: new style.Icon({
+      src: RadarXMB,
+    }),
+  }))
+  return _style
+}
+const radarCircle = (val, map) => {
+  let _style: any = []
+  for (let i = Number(val.scannedAreaEnd); i > 0; i -= 1000) {
+    _style.push(new style.Style({
+      geometry: new geom.Circle(new format.WKT().readFeature(val.location).getGeometry().getCoordinates(), i / map.getView().getProjection().getMetersPerUnit()),
+      stroke: new style.Stroke({
+        color: '#00ff00',
+        width: 1,
+        lineDash: [5, 5]
+      }),
+      fill: new style.Fill({
+        color: "rgb(255, 255, 255, 0.2)"
+      })
+    }))
+  }
+  return _style
+}
+const radarArea = (val, map) => {
+  let _style: any = []
+  _style.push(new style.Style({
+    stroke: new style.Stroke({
+      color: val.segmentColor,
+      width: val.segmentWidth,
+    }),
+    fill: new style.Fill({
+      color: val.regionalColor,
+    }),
+  }))
+  const center = extent.getCenter(extent.boundingExtent(new format.WKT().readFeature(val.location).getGeometry().getCoordinates()[0]))
+  _style.push(new style.Style({
+    geometry: new geom.Point(center),
+    text: new style.Text({
+      text: val.name,
+      textAlign: "left", // 位置
+      textBaseline: "center", // 基准线
+      fill: new Fill({
+        color: '#ffffff'
+      }),
+      stroke: new Stroke({
+        color: "#D26CDB",
+        width: 3
+      }),
+    })
+  }))
+  return _style
+}
+export {
+  radarPoint,
+  radarCircle,
+  radarArea,
+}

File diff suppressed because it is too large
+ 20 - 0
src/views/element/img/element-radar-xmb.svg


+ 300 - 13
src/views/element/index-ol.vue

@@ -28,12 +28,23 @@
         </template>
       </el-tree-v2>
     </div>
-    <div class="map">
+    <div class="map-block">
       <EasyMapComponent
         class="map"
         layout="info"
         @easyMapLoad="mapLoad"
       />
+      <div class="hide-list">
+        <div class="hover-main">
+          <template v-for="(item, index) in polyLineHideList">
+            <div class="hover-item">
+              <div class="hover-item-label __hover" @click="webglPolyLineLayer.getSource().addFeature(item.feature), polyLineHideList.splice(index, 1)">恢复:</div>
+              <div class="hover-item-value">{{item.name}}</div>
+            </div>
+          </template>
+
+        </div>
+      </div>
     </div>
     <div ref="ref_mapHover" class="hover-info">
       <div class="hover-main">
@@ -47,6 +58,25 @@
         </div>
       </div>
     </div>
+    <div class="hover-info" ref="ref_mapClick" style="z-index: 10">
+      <div class="hover-info-head">
+        <span>要素列表</span>
+      </div>
+      <div class="hover-info-close __hover" @click="mapClick.overlay?.setPosition(undefined)">
+        <img src="@/components/easyMap/images/close.png" alt=""/>
+      </div>
+      <div class="hover-main" v-if="mapClick.list" style="max-height: 250px;overflow-y: auto;">
+        <template v-for="item in mapClick.list">
+          <div class="hover-item">
+            <div class="hover-item-label">{{item.type}}:</div>
+            <div class="hover-item-value">
+              {{item.name}}
+              <el-link href="javascript:;" type="danger" @click.stop="onActive(item)">突出</el-link>
+            </div>
+          </div>
+        </template>
+      </div>
+    </div>
   </div>
 </template>
 
@@ -74,10 +104,15 @@ import * as layer from "ol/layer";
 import * as format from "ol/format";
 import * as ol from "ol";
 import WebGLVectorLayerRenderer from 'ol/renderer/webgl/VectorLayer';
+import * as ElementStyle from "./elementStyle";
+import * as turf from '@turf/turf'
+import * as style from "ol/style";
+
 
 class WebGLLayer extends layer.Layer {
   createRenderer() {
     return new WebGLVectorLayerRenderer(this, {
+      disableHitDetection: false,
       style: {
         'stroke-color': ['*', ['get', 'lineColor'], [220, 220, 220]],
         'stroke-width': ['get', 'lineWidth'],
@@ -102,13 +137,21 @@ export default defineComponent({
       treeText: '',
       webglPointLayer: <any>null,
       webglPolyLineLayer: <any>null,
+      radarLayer: <any>null,
+      polyLineActiveLayer: <any>null,
       mapHover: {
         info: {},
         overlay: <any>null
-      }
+      },
+      mapClick: {
+        list: {},
+        overlay: <any>null
+      },
+      polyLineHideList: <any>[]
     })
     const ref_tree = ref()
     const ref_mapHover = ref()
+    const ref_mapClick = ref()
     const mapLoad = (map, func) => {
       state.map = map
       state.mapFunc = func
@@ -122,12 +165,12 @@ export default defineComponent({
           mapId: key,
           mapLabel: pName,
           children: [],
-          _show: true
+          _show: key !== 'radarList'
         }
         xqysJson.data[key].forEach(v => {
           v.mapId = key + '_' + v[idKey]
           v.mapLabel = v[labelKey]
-          v._show = true
+          v._show = key !== 'radarList'
           obj.children.push(v)
         })
         state.treeData.push(obj)
@@ -150,6 +193,27 @@ export default defineComponent({
       setSourceData()
     }
     const initMap = () => {
+      state.radarLayer = new layer.Vector({
+        zIndex: 40,
+      })
+      state.map.addLayer(state.radarLayer)
+      state.polyLineActiveLayer = new layer.Vector({
+        zIndex: 39,
+        source: new source.Vector(),
+        style: (feat: any) => {
+          feat.setStyle(new style.Style({
+            stroke: new style.Stroke({
+              color: '#ff0000',
+              width: feat.get('lineWidth'),
+              lineDash: feat.get('lineDasharray')
+            }),
+            fill: new style.Fill({
+              color: 'rgba(255,0,0,0.3)',
+            }),
+          }))
+        }
+      })
+      state.map.addLayer(state.polyLineActiveLayer)
       state.mapHover.overlay = new ol.Overlay({
         id: 'ref_mapHover',
         element: ref_mapHover.value,
@@ -162,14 +226,35 @@ export default defineComponent({
         }
       })
       state.map.addOverlay(state.mapHover.overlay)
+      state.mapClick.overlay = new ol.Overlay({
+        id: 'ref_mapClick',
+        element: ref_mapClick.value,
+        autoPan: false,
+        offset: [0, -30],
+        positioning: 'bottom-center',
+        stopEvent: true,
+        insertFirst: false,
+        autoPanAnimation: {
+          duration: 250
+        }
+      })
+      state.map.addOverlay(state.mapClick.overlay)
       state.map.on('pointermove', function (evt) {
         if (evt.dragging) {
           return;
         }
         const pixel = state.map.getEventPixel(evt.originalEvent);
-        const feature = state.map.forEachFeatureAtPixel(pixel, function (feature) {
+        let feature: any = state.map.forEachFeatureAtPixel(pixel, function (feature) {
           return feature;
         });
+        const point = state.map.forEachFeatureAtPixel(pixel, function (feature) {
+          if (feature.get('geomType') === 'Point') {
+            return feature;
+          }
+        });
+        if (point) {
+          feature = point
+        }
         if (feature) {
           state.mapHover.info = {
             name: feature.get('name'),
@@ -180,8 +265,58 @@ export default defineComponent({
           state.mapHover.overlay.setPosition(undefined)
         }
       });
+      state.map.on('contextmenu', function (evt) {
+        evt.preventDefault()
+        if (evt.dragging) {
+          return;
+        }
+        const pixel = state.map.getEventPixel(evt.originalEvent);
+        const feature = state.map.forEachFeatureAtPixel(pixel, function (feature) {
+          return feature;
+        });
+        if (feature?.getGeometry().getType() !== 'Point') {
+          state.mapHover.overlay.setPosition(undefined)
+          state.webglPolyLineLayer.getSource().removeFeature(feature)
+          state.polyLineHideList.push({
+            name: feature.get('name'),
+            feature: feature
+          })
+        }
+      })
+      state.map.on('singleclick', function (evt) {
+        if (evt.dragging) {
+          return;
+        }
+        const feats: any = []
+        state.webglPolyLineLayer.getSource().getFeatures().forEach(v => {
+          if (v.get('geomType') === 'Polygon') {
+            const poly = turf.polygon(v.getGeometry().getCoordinates());
+            if (turf.booleanPointInPolygon(turf.point(evt.coordinate), poly)) {
+              feats.push(v)
+            }
+          } else if (v.get('geomType') === 'LineString') {
+            const circle = turf.circle(evt.coordinate, 0.5, {steps: 4, units: 'kilometers', properties: {foo: 'bar'}})
+            const line = turf.lineString(v.getGeometry().getCoordinates());
+            if (turf.booleanCrosses(circle, line)) {
+              feats.push(v)
+            }
+          }
+        })
+        if (feats.length > 0) {
+          state.mapClick.list = feats.map(v => {
+            return {
+              name: v.get('name'),
+              type: v.get('geomType'),
+              feature: v
+            }
+          })
+          state.mapClick.overlay.setPosition(evt.coordinate)
+        } else {
+          state.mapClick.overlay.setPosition(undefined)
+        }
+      });
     }
-    const style = {
+    const webglStyle = {
       'icon-src': jlt,
       'icon-width': 800,
       'icon-height': 600,
@@ -209,13 +344,88 @@ export default defineComponent({
     const setSourceData = () => {
       const points: any = []
       const polyLines: any = []
+      const radars: any = []
       state.treeData.forEach(p => {
         if (p.mapId === 'deptInfo') {
+          p.children.forEach(v => {
+            if (v._show) {
+              try {
+                if (v.pointCoordinate) {
+                  const feat: any = new format.WKT().readFeature(v.pointCoordinate)
+                  feat.set('layerType', 'webgl')
+                  feat.set('name', p.mapLabel + '_' + v.mapLabel)
+                  feat.set('geomType', feat.getGeometry().getType())
+                  feat.set('iconImage', 'radarListImg')
+                  points.push(feat)
+                }
+                if (v.regionalLocation) {
+                  const feat: any = new format.WKT().readFeature(v.regionalLocation)
+                  feat.set('layerType', 'webgl')
+                  feat.set('name', p.mapLabel + '_' + v.mapLabel)
+                  feat.set('geomType', feat.getGeometry().getType())
+                  let fillColor = 'rgba(20, 129, 241, 0.3)'
+                  let lineColor = '#2860F1'
+                  let lineWidth = 1
+                  let lineDasharray = globalLineDash[0]
+                  if (v.regionalColor) {
+                    fillColor = v.regionalColor
+                  }
+                  if (v.segmentColor) {
+                    lineColor = v.segmentColor
+                  }
+                  if (v.segmentWidth) {
+                    lineWidth = v.segmentWidth
+                  }
+                  if (v.segmentType) {
+                    lineDasharray = globalLineDash[Number(v.segmentType)]
+                  }
+                  feat.set('fillColor', fillColor)
+                  feat.set('lineColor', lineColor)
+                  feat.set('lineWidth', lineWidth)
+                  feat.set('lineDasharray', lineDasharray)
+                  polyLines.push(feat)
+                }
+              } catch (e) {
+              }
+            }
+          })
+        } else if (p.mapId === 'radarList') {
+          p.children.forEach(v => {
+            if (v._show) {
+              try {
+                if (v.location) {
+                  const feat: any = new format.WKT().readFeature(v.location)
+                  feat.set('name', p.mapLabel + '_' + v.mapLabel)
+                  feat.set('geomType', feat.getGeometry().getType())
+                  feat.setStyle(ElementStyle.radarPoint())
+                  radars.push(feat)
+                }
+                if (v.radarShielding?.length > 0) {
+                  v.radarShielding.forEach(s => {
+                    const feat: any = new format.WKT().readFeature(s.location)
+                    feat.set('name', p.mapLabel + '_' + v.mapLabel + '_' + s.name)
+                    feat.set('geomType', feat.getGeometry().getType())
+                    feat.setStyle(ElementStyle.radarArea(s, state.map))
+                    radars.push(feat)
+                  })
+                }
+                if (Number(v.scannedAreaEnd) > 0) {
+                  const feat: any = new format.WKT().readFeature(v.location)
+                  feat.set('name', p.mapLabel + '_' + v.mapLabel + '_半径')
+                  feat.set('geomType', feat.getGeometry().getType())
+                  feat.setStyle(ElementStyle.radarCircle(v, state.map))
+                  radars.push(feat)
+                }
+              } catch (e) {
+              }
+            }
+          })
         } else {
           p.children.forEach(v => {
             if (v._show) {
               try {
                 const feat: any = new format.WKT().readFeature(v.location)
+                feat.set('layerType', 'webgl')
                 feat.set('name', p.mapLabel + '_' + v.mapLabel)
                 feat.set('geomType', feat.getGeometry().getType())
                 if (feat.getGeometry().getType() === 'Point') {
@@ -257,8 +467,6 @@ export default defineComponent({
                   }
                   feat.set('fillColor', fillColor)
                   feat.set('lineColor', lineColor)
-                  feat.set('_fillColor', fillColor)
-                  feat.set('_lineColor', lineColor)
                   feat.set('lineWidth', lineWidth)
                   feat.set('lineDasharray', lineDasharray)
                   polyLines.push(feat)
@@ -274,7 +482,7 @@ export default defineComponent({
         state.webglPolyLineLayer.dispose()
       }
       state.webglPolyLineLayer = new WebGLLayer({
-        zIndex: 15,
+        zIndex: 20,
         source: new source.Vector({
           features: polyLines
         }),
@@ -285,13 +493,19 @@ export default defineComponent({
         state.webglPointLayer.dispose()
       }
       state.webglPointLayer = new layer.WebGLPoints({
-        zIndex: 15,
+        zIndex: 30,
         source: new source.Vector({
           features: points
         }),
-        style: style
+        style: webglStyle
       })
       state.map.addLayer(state.webglPointLayer)
+      if (state.radarLayer) {
+        state.radarLayer.setSource(new source.Vector({
+          features: radars
+        }))
+      }
+      state.polyLineActiveLayer?.getSource()?.clear()
     }
     const filterMethod = (text: string, node: any) => {
       return node.mapLabel?.includes(text)
@@ -333,6 +547,11 @@ export default defineComponent({
       })
       setSourceData()
     }
+    const onActive = (item) => {
+      state.polyLineActiveLayer.getSource().clear()
+      state.polyLineActiveLayer.getSource().addFeature(item.feature)
+      console.log(item)
+    }
     onMounted(() => {
       console.log(xqysJson)
       // console.log(WebGLLayer)
@@ -348,7 +567,9 @@ export default defineComponent({
       onHide,
       onShowAll,
       onHideAll,
-      ref_mapHover
+      ref_mapHover,
+      ref_mapClick,
+      onActive
     }
   },
 })
@@ -363,10 +584,76 @@ export default defineComponent({
     width: 400px;
     height: 100%;
   }
-  .map {
+  .map-block {
     flex: 1;
+    position: relative;
+    .map {
+      flex: 1;
+    }
+    .hide-list {
+      $footH: 10px;
+      min-width: 300px;
+      max-height: 300px;
+      overflow-y: auto;
+      background: linear-gradient(180deg, #3874C9 0%, #0043C4 100%);
+      border-radius: 0px 4px 4px 4px;
+      position: absolute;
+      right: 0;
+      top: 0;
+      display: flex;
+      justify-content: center;
+      .hover-info-head {
+        padding: 0 4px;
+        height: 18px;
+        position: absolute;
+        top: -18px;
+        left: 0;
+        font-size: 12px;
+        font-family: PingFang SC;
+        font-weight: 500;
+        color: #FFFFFF;
+        display: flex;
+        align-items: center;
+        line-height: 8px;
+        background: linear-gradient(180deg, #3874C9 0%, #0043C4 100%);
+        border-radius: 2px 2px 0 0;/* 设置圆角 */
+        >img {
+          margin: 0 4px 0 6px;
+        }
+      }
+      .hover-info-close {
+        position: absolute;
+        right: 0;
+        top: -16px;
+      }
+      .hover-main {
+        width: 100%;
+        height: auto;
+        padding: 10px;
+        .hover-item {
+          display: flex;
+          .hover-item-label {
+            min-width: 42px;
+            font-size: 14px;
+            font-family: PingFang SC;
+            font-weight: 600;
+            color: #08FFFF;
+            line-height: 20px;
+          }
+          .hover-item-value {
+            flex: 1;
+            font-size: 14px;
+            font-family: PingFang SC;
+            font-weight: 400;
+            color: #FFFFFF;
+            line-height: 20px;
+          }
+        }
+      }
+    }
   }
 }
+
 .hover-info {
   $footH: 10px;
   width: 300px;

+ 2 - 2
src/views/index.vue

@@ -3,8 +3,8 @@
     <el-button @click="$router.push('/mock-track')">模拟轨迹</el-button>
     <el-button @click="$router.push('/track-status')">船舶轨迹状态查询</el-button>
     <el-button @click="$router.push('/ship-test')">船舶测试</el-button>
-    <el-button @click="$router.push('/ship-playback')">船舶回放</el-button>
-<!--    <el-button @click="$router.push('/element')">辖区要素</el-button>-->
+    <el-button @click="$router.push('/element')">辖区要素</el-button>
+<!--    <el-button @click="$router.push('/ship-playback')">船舶回放</el-button>-->
   </div>
 </template>