Sfoglia il codice sorgente

粤政图绘制更新

Hwf 3 mesi fa
parent
commit
f9e8531415
3 ha cambiato i file con 297 aggiunte e 29 eliminazioni
  1. BIN
      src/assets/images/car.png
  2. 57 0
      src/utils/index.ts
  3. 240 29
      src/utils/olMap/olMap.ts

BIN
src/assets/images/car.png


+ 57 - 0
src/utils/index.ts

@@ -63,3 +63,60 @@ export const hexToRgba = (hex, alpha) => {
     // 返回 RGBA 字符串
     return `rgba(${r}, ${g}, ${b}, ${alpha || 1})`;
 };
+
+export const rgbToRgba = (rgbString, alpha) => {
+    if (!rgbString) return '';
+    // 去掉字符串两端的空格
+    rgbString = rgbString.trim();
+
+    // 使用正则表达式匹配 rgb 格式的数字部分
+    const rgbMatch = rgbString.match(/\d+/g);
+
+    // 如果没有匹配到 rgb 格式,返回 null
+    if (!rgbMatch || rgbMatch.length !== 3) {
+        return null;
+    }
+
+    // 将匹配到的数字部分转换为整数
+    const r = parseInt(rgbMatch[0], 10);
+    const g = parseInt(rgbMatch[1], 10);
+    const b = parseInt(rgbMatch[2], 10);
+
+    // 拼接 rgba 字符串
+    const rgbaString = `rgba(${r}, ${g}, ${b}, ${alpha})`;
+
+    return rgbaString;
+};
+
+export const initDrag = (el, scaleX = 1, scaleY = 1) => {
+    if (!el) return;
+    el.style.position = 'fixed';
+    el.style.cursor = 'move';
+
+    const dragDom = el;
+
+    let disX = 0; // 鼠标按下时,鼠标与拖拽元素的左边距
+    let disY = 0; // 鼠标按下时,鼠标与拖拽元素的上边距
+
+    dragDom.onmousedown = (e) => {
+        // 鼠标按下,计算当前元素距离可视区的距离
+        disX = e.clientX / scaleX - dragDom.offsetLeft;
+        disY = e.clientY / scaleY - dragDom.offsetTop;
+
+        // 鼠标移动事件
+        document.onmousemove = function (e) {
+            // 通过事件委托,计算移动的距离
+            const l = e.clientX / scaleX - disX;
+            const t = e.clientY / scaleY - disY;
+            // 移动当前元素
+            dragDom.style.left = `${l + 'px'}`;
+            dragDom.style.top = `${t + 'px'}`;
+        };
+
+        // 鼠标松开事件
+        document.onmouseup = function () {
+            document.onmousemove = null;
+            document.onmouseup = null;
+        };
+    };
+};

+ 240 - 29
src/utils/olMap/olMap.ts

@@ -33,10 +33,11 @@ import Overlay from 'ol/Overlay';
 import { Draw, Select } from 'ol/interaction';
 import { click } from 'ol/events/condition';
 import Circle from 'ol/geom/Circle';
-import { deepClone, hexToRgba } from '@/utils';
+import { deepClone, getRgba, hexToRgba, initDrag, rgbToRgba } from '@/utils';
 import { createBox } from 'ol/interaction/Draw';
 import * as turf from '@turf/turf';
 import { nanoid } from 'nanoid';
+import carImg from '@/assets/images/car.png';
 
 const tk = 'a8df87f1695d224d2679aa805c1268d9';
 const commonUrl = import.meta.env.VITE_APP_BASE_API2 + 'api/oneShare/proxyHandler/gd/';
@@ -86,6 +87,10 @@ export class olMap {
   // 显示信息框
   private infoWindow;
   private select;
+  // 车辆轨迹
+  private carLayer;
+  private carFeature;
+  private traceFeature;
 
   constructor(options) {
     this.options = options;
@@ -316,6 +321,7 @@ export class olMap {
       marker: 'Point',
       text: 'Point'
     };
+    const data = getRgba(newOptions.color);
     let geometryFunction = null;
     if (newOptions.graphicsType === 'rectangle') {
       // 绘制矩形的方法
@@ -336,11 +342,12 @@ export class olMap {
       } else {
         this.drawOptions = {
           type: newOptions.graphicsType,
-          strokeColor: newOptions.color,
+          title: newOptions.title,
+          strokeColor: !!data.color ? data.color : newOptions.color,
           strokeOpacity: 1,
           strokeWeight: 1,
-          fillColor: newOptions.color,
-          fillOpacity: newOptions.drawType === '1' ? '0' : '0.5',
+          fillColor: data.color,
+          fillOpacity: data.opacity,
           strokeStyle: 'solid'
         };
         if (newOptions.graphicsType === 'marker') {
@@ -352,11 +359,11 @@ export class olMap {
         // 创建绘制交互
         let style = new Style({
           stroke: new Stroke({
-            color: hexToRgba(this.drawOptions.strokeColor, this.drawOptions.strokeOpacity),
+            color: rgbToRgba(this.drawOptions.strokeColor, this.drawOptions.strokeOpacity),
             width: this.drawOptions.strokeWeight
           }),
           fill: new Fill({
-            color: hexToRgba(this.drawOptions.fillColor, this.drawOptions.fillOpacity)
+            color: rgbToRgba(this.drawOptions.fillColor, this.drawOptions.fillOpacity)
           })
         });
         this.drawTool = new Draw({
@@ -570,27 +577,34 @@ export class olMap {
       });
       // 设置自定义属性
       feature.set('pointer', true);
-      // 定义样式
-      const style = new Style({
-        image: new Icon({
-          anchor: [0.5, 0.5],
-          scale: 0.146,
-          anchorXUnits: 'fraction',
-          anchorYUnits: 'pixels',
-          src: point.icon
-        }),
-        text: new Text({
-          text: point.name,
-          fill: new Fill({
-            color: '#000'
+      const img = new Image();
+      img.onload = () => {
+        // 图片加载完成后,可以访问其 width 和 height 属性
+        const width = img.width;
+        const height = img.height;
+        const style = new Style({
+          image: new Icon({
+            anchor: [0.5, 0.5],
+            anchorXUnits: 'fraction',
+            anchorYUnits: 'pixels',
+            src: point.icon,
+            size: [width, height],
+            scale: !!point.size[0] ? point.size[0] / width : 1
           }),
-          stroke: new Stroke({
-            color: '#fff',
-            width: 3
+          text: new Text({
+            text: point.name,
+            fill: new Fill({
+              color: '#000'
+            }),
+            stroke: new Stroke({
+              color: '#fff',
+              width: 3
+            })
           })
-        })
-      });
-      feature.setStyle(style);
+        });
+        features.setStyle(style);
+      };
+      img.src = point.icon; // 设置图片的 URL,触发加载
       features.push(feature);
       this.markers.push(point);
     });
@@ -620,6 +634,7 @@ export class olMap {
       });
     }
     this.infoWindow.setPosition(position);
+    initDrag(this.infoWindow.element);
     this.map.addOverlay(this.infoWindow);
   }
 
@@ -898,11 +913,11 @@ export class olMap {
     feature.setStyle(
         new Style({
           stroke: new Stroke({
-            color: hexToRgba(data.strokeColor, data.strokeOpacity),
+            color: rgbToRgba(data.strokeColor, data.strokeOpacity),
             width: data.strokeWeight
           }),
           fill: new Fill({
-            color: hexToRgba(data.fillColor, data.fillOpacity)
+            color: rgbToRgba(data.fillColor, data.fillOpacity)
           })
         })
     );
@@ -915,17 +930,213 @@ export class olMap {
     feature.setStyle(
         new Style({
           stroke: new Stroke({
-            color: hexToRgba(data.strokeColor, data.strokeOpacity),
+            color: rgbToRgba(data.strokeColor, data.strokeOpacity),
+            width: data.strokeWeight
+          }),
+          fill: new Fill({
+            color: rgbToRgba(data.fillColor, data.fillOpacity)
+          })
+        })
+    );
+    this.drawVector.getSource().addFeature(feature);
+    return feature;
+  }
+  createLineString(data) {
+    const lineString = new LineString(data.path);
+    const feature = new Feature(lineString);
+    feature.setStyle(
+        new Style({
+          stroke: new Stroke({
+            color: rgbToRgba(data.strokeColor, data.strokeOpacity),
             width: data.strokeWeight
           }),
           fill: new Fill({
-            color: hexToRgba(data.fillColor, data.fillOpacity)
+            color: rgbToRgba(data.fillColor, data.fillOpacity)
           })
         })
     );
     this.drawVector.getSource().addFeature(feature);
     return feature;
   }
+  trackPlayback(lineArr) {
+    if (!!this.carFeature) {
+      this.carLayer.getSource().removeFeature(this.carFeature);
+    }
+    if (!!this.traceFeature) {
+      this.carLayer.getSource().removeFeature(this.traceFeature);
+    }
+    const getAngle = (point1, point2) => {
+      let arc = 0;
+      if (point2 && point2.length && point1 && point1.length) {
+        if ((point2[0] - point1[0] >= 0 && point2[1] - point1[1] >= 0) || (point2[0] - point1[0] < 0 && point2[1] - point1[1] > 0)) {
+          arc = Math.atan((point2[0] - point1[0]) / (point2[1] - point1[1]));
+        } else if ((point2[0] - point1[0] > 0 && point2[1] - point1[1] < 0) || (point2[0] - point1[0] < 0 && point2[1] - point1[1] < 0)) {
+          arc = Math.PI + Math.atan((point2[0] - point1[0]) / (point2[1] - point1[1]));
+        }
+      }
+      return arc;
+    };
+    let lastTime = Date.now();
+    const source = new VectorSource();
+    let distance = 0;
+    const angle = getAngle(lineArr[0], lineArr[1]);
+    const speed = 500;
+    let animationFlag = false;
+    this.carFeature = new Feature({
+      geometry: new Point(lineArr[0])
+    });
+    const icon = new Icon({
+      crossOrigin: 'anonymous',
+      src: carImg,
+      width: 26,
+      height: 52,
+      rotation: angle
+    });
+    this.carFeature.setStyle(
+        new Style({
+          image: icon
+        })
+    );
+    this.traceFeature = new Feature({
+      geometry: new LineString(lineArr)
+    });
+    let route = new LineString(lineArr);
+    this.carLayer = new VectorLayer({
+      source: source,
+      style: new Style({
+        stroke: new Stroke({
+          color: 'rgb(37,232,142)',
+          width: 5
+        })
+      })
+    });
+    this.carLayer.getSource().addFeatures([this.carFeature, this.traceFeature]);
+    const move = (e) => {
+      const time = e.frameState.time;
+      // 时间戳差(毫秒)
+      const elapsedTime = time - lastTime;
+      // 距离(其实是比例的概念)
+      distance = distance + (speed * elapsedTime) / 1e6;
+      if (distance >= 1) {
+        distance = 0;
+        animationFlag = false;
+        stopAnimation();
+        return;
+      }
+      // 保存当前时间
+      lastTime = time;
+      // 上次坐标
+      const lastCoord = this.carFeature.getGeometry().getCoordinates();
+      // 获取新位置的坐标点
+      const curCoord = route.getCoordinateAt(distance);
+      // 设置新坐标
+      this.carFeature.getGeometry().setCoordinates(curCoord);
+      this.map.getView().setCenter(curCoord);
+      // 设置角度
+      this.carFeature.getStyle().getImage().setRotation(getAngle(lastCoord, curCoord));
+      // 调用地图渲染
+      this.map.render();
+    };
+
+    const stopAnimation = () => {
+      this.carLayer.un('postrender', move);
+    };
+    this.map.addLayer(this.carLayer);
+    this.carLayer.on('postrender', move);
+    // 触发地图渲染
+    const geo = this.carFeature.getGeometry().clone();
+    this.carFeature.setGeometry(geo);
+  }
+  drawData(data) {
+    const res = [];
+    data.forEach((item) => {
+      let graphic;
+      if (['rectangle', 'polygon', 'anyLine'].includes(item.type)) {
+        graphic = this.createPolygon(item);
+        graphic.set('id', item.id);
+        res.push(graphic);
+      } else if (item.type === 'circle') {
+        graphic = this.createCircle(item);
+        graphic.set('id', item.id);
+        res.push(graphic);
+      } else if (item.type === 'straightLine') {
+        graphic = this.createLineString(item);
+        graphic.set('id', item.id);
+        res.push(graphic);
+      } else if (item.type === 'text') {
+        const { text } = this.addText(item);
+        res.push(text);
+      } else if (item.type === 'measureArea') {
+        graphic = this.createPolygon(item);
+        graphic.set('id', item.id);
+        const style = new Style({
+          stroke: new Stroke({
+            color: rgbToRgba(item.strokeColor, item.strokeOpacity),
+            width: item.strokeWeight
+          }),
+          fill: new Fill({
+            color: rgbToRgba(item.fillColor, item.fillOpacity)
+          }),
+          text: new Text({
+            text: '区域面积' + item.area.toFixed(2) + '平方米',
+            font: '14px Calibri,sans-serif',
+            fill: new Fill({ color: '#000' }),
+            stroke: new Stroke({
+              color: '#fff',
+              width: 3
+            }),
+            overflow: true
+          })
+        });
+
+        graphic.setStyle(style);
+        res.push(graphic);
+      } else if (item.type === 'marker') {
+        // 创建标注点
+        const marker = new Feature({
+          // 必须是数字类型,字符串不识别
+          geometry: new Point([item.longitude, item.latitude]),
+          // name: item.name,
+          // icon: item.icon,
+          // imageHover: item.imageHover,
+          // size: item.size,
+          pointer: true
+        });
+        marker.set('id', item.id);
+        const img = new Image();
+        img.onload = () => {
+          // 图片加载完成后,可以访问其 width 和 height 属性
+          const width = img.width;
+          const height = img.height;
+          const style = new Style({
+            image: new Icon({
+              anchor: [0.5, 0.5],
+              anchorXUnits: 'fraction',
+              anchorYUnits: 'pixels',
+              src: item.icon,
+              size: [width, height],
+              scale: !!item.size[0] ? item.size[0] / width : 1
+            }),
+            text: new Text({
+              text: item.name,
+              fill: new Fill({
+                color: '#000'
+              }),
+              stroke: new Stroke({
+                color: '#fff',
+                width: 3
+              })
+            })
+          });
+          marker.setStyle(style);
+        };
+        img.src = item.icon; // 设置图片的 URL,触发加载
+        this.drawVector.getSource().addFeature(marker);
+        res.push(marker);
+      }
+    });
+    return res;
+  }
   getVectorLayer() {
     return this.vectorLayer;
   }