美文网首页
openlayers+vue3 根据坐标集合绘制轨迹

openlayers+vue3 根据坐标集合绘制轨迹

作者: 月下小酌_dbd5 | 来源:发表于2023-08-18 13:50 被阅读0次
    • 绘制组件 MapTrackDraw.vue
    <template>
    <div></div>
    </template>
    <script lang='ts' setup>
    import { ref, reactive,onMounted, nextTick } from 'vue'
    
    import "ol/ol.css";
    import { Map, View, Feature, Overlay } from "ol";
    import { Vector as VectorLayer } from "ol/layer";
    import { Vector as VectorSource } from "ol/source";
    import { Point, LineString } from "ol/geom.js";
    import { Icon, Fill, Stroke, Style, Circle } from "ol/style";
    import { getVectorContext } from "ol/render";
    import { ElMessage } from 'element-plus';
    
    interface Props {
      map:any,
      view:any
    }
    const { map,view } = defineProps<Props>()
    
    let geometryMove:any = {}
    let featureMove:any = {}
    let vectorLayer:any = ref(null)
    
    let distance:number = 0
    let lastTime:number = 0
    let speed:number = 0.1
    
    let styles:any = {
      route: new Style({
              stroke: new Stroke({
                width: 8,
                color: "green",
              }),
            }),
            iconStart: new Style({
              image: new Icon({
                anchor: [0.5, 1],
                src: require('@/assets/images/start_point.png'),
                scale: 1, //设置大小
              }),
            }),
            iconEnd: new Style({
              image: new Icon({
                anchor: [0.5, 1],
                src: require('@/assets/images/end_point.png'),
                scale: 1, //设置大小
              }),
            }),
            
            featureMove: new Style({
              
              image: new Icon({
                anchor: [0.5, 1],
                src: require('@/assets/images/walk-icon.png'),
                scale: 1, //设置大小
              }),
    
              // image: new Circle({
              //   radius: 7,
              //   fill: new Fill({ color: "black" }),
              //   stroke: new Stroke({
              //     color: "white",
              //     width: 2,
              //   }),
              // }),
    
    
            }),
    }
    
    let route:any = null
    
    const drawHandle = (coordinateArr:any[]) => { 
      clearDrawHandle()
      view.setCenter(coordinateArr[0])
      route = new LineString(coordinateArr)
      geometryMove = new Point(route.getFirstCoordinate())
      featureMove = new Feature({
        type: "featureMove",
        geometry: geometryMove,
      })
      vectorLayer.value = new VectorLayer({
        source: new VectorSource({
          features: [
            new Feature({
              type: "route",
              geometry: route,
            }),
            featureMove,
            new Feature({
              type: "iconStart",
              geometry: new Point(route.getFirstCoordinate()),
            }),
            new Feature({
              type: "iconEnd",
              geometry: new Point(route.getLastCoordinate()),
            }),
          ],
        }),
        style: (feature:any) => {
          if(feature.get("type") == 'route') {
            feature.setStyle(arrowLineStyles)
            return 
          }
    
          return styles[feature.get("type")];
        },
      })
    
      map.addLayer(vectorLayer.value)
    
    }
    // 清除绘制
    const clearDrawHandle = () => {
      if(vectorLayer.value) {
        // vectorLayer.value.getSource().clear()
        map.removeLayer(vectorLayer.value)
        vectorLayer.value = null
      }
    }
    
    // 移动 
    const moveFeature = (e:any) => {
      let time = e.frameState.time;
      distance =
        (distance + (speed * (time - lastTime)) / 1000) % 1; //%2表示:起止止起;%1表示:起止起止
    
      lastTime = time;
    
      const currentCoordinate = route.getCoordinateAt(
        distance > 1 ? 2 - distance : distance
      );
      geometryMove.setCoordinates(currentCoordinate);
      const vectorContext = getVectorContext(e);
    
    
      vectorContext.setStyle(styles.featureMove);
      vectorContext.drawGeometry(geometryMove);
      map.render();
    }
    
    // 动画开始
    const startAnimation = () => {
      if(vectorLayer.value) {
        lastTime = Date.now();
        vectorLayer.value.on("postrender", moveFeature);
        featureMove.setGeometry(null); //必须用null,不能用{}
      }else {
        ElMessage.warning('请先绘制路线!')
      }
    }
    
    // 动画结束
    const stopAnimation = () => {
      featureMove.setGeometry(geometryMove);
      vectorLayer.value.un("postrender", moveFeature);
    }
    
    
    // 箭头样式
    const arrowLineStyles = (feature:any, resolution:any) => {
      let styles = [];
      // 线条样式
      let backgroundLineStyle = new Style({
        stroke: new Stroke({
          width: 10,
          color: "green",
        }),
      });
      styles.push(backgroundLineStyle);
      let geometry = feature.getGeometry();
      // 获取线段长度
      const length = geometry.getLength();
      // 箭头间隔距离(像素)
      const step = 50;
      // 将间隔像素距离转换成地图的真实距离
      const StepLength = step * resolution;
      // 得到一共需要绘制多少个 箭头
      const arrowNum = Math.floor(length / StepLength);
      const rotations:any = [];
      const distances = [0];
      geometry.forEachSegment(function (start:any, end:any) {
        let dx = end[0] - start[0];
        let dy = end[1] - start[1];
        let rotation = Math.atan2(dy, dx);
        distances.unshift(Math.sqrt(dx ** 2 + dy ** 2) + distances[0]);
        rotations.push(rotation);
      });
      // 利用之前计算得到的线段矢量信息,生成对应的点样式塞入默认样式中
      // 从而绘制内部箭头
      for (let i = 1; i < arrowNum; ++i) {
        const arrowCoord = geometry.getCoordinateAt(i / arrowNum);
        const d = i * StepLength;
        const grid = distances.findIndex((x) => x <= d);
    
        styles.push(
          new Style({
            geometry: new Point(arrowCoord),
            image: new Icon({
              src: require('@/assets/images/right-icon.png'),
              opacity: 1,
              anchor: [0.5, 0.5],
              rotateWithView: false,
              // 读取 rotations 中计算存放的方向信息
              rotation: -rotations[distances.length - grid - 1],
              scale: 0.8,
            }),
          })
        );
      }
      return styles;
    }
    
    defineExpose({
      drawHandle,
      clearDrawHandle,
      startAnimation,
      stopAnimation
    })
    
    
    </script>
    <style scoped lang='less'>
    </style>
    
    • 使用组件
    <template>
      <div class="map-box" id="map" :key="keyMap">
        <MapTrackDraw ref="mapTrackDrawRef" :map="myMap" :view="myView" :key="myMap" />
        <el-button type="primary" @click="drawHandle">绘制</el-button>
        <el-button type="primary" @click="clearDrawHandle">清除绘制</el-button>
        <el-button type="primary" @click="startAnimation()">路线动画开始</el-button>
        <el-button type="primary" @click="stopAnimation()">路线动画暂停</el-button>
      </div>
    </template>
    <script lang='ts' setup>
    //引入组件
    import MapTrackDraw from '@/views/components/MapTrackDraw.vue'
    
    import { ref, reactive, onMounted, nextTick,watch } from 'vue'
    //导入相关配置信息
    import 'ol/css';
    import { Map, View, Feature } from 'ol'
    import { Style, Icon } from 'ol/style'
    import { Point } from 'ol/geom';
    import { Vector as SourceVec, XYZ, } from 'ol/source'
    import { Vector as LayerVec } from 'ol/layer'
    import TileLayer from 'ol/layer/Tile'
    import { defaults as defaultControls, MousePosition, } from "ol/control"
    
    let trackData = [
            [120.13422983645752, 30.331830188982277],
            [120.13446587085083, 30.330585643999367],
            [120.13459461688355, 30.33000628685215],
            [120.13508814334229, 30.329941913835793],
            [120.13598936557129, 30.33002774452427],
            [120.1382424211438, 30.33025305008152],
            [120.13861793040589, 30.329394743196755],
            [120.13873594760254, 30.328557893984108],
            [120.13883250712708, 30.327688858263283],
            [120.1377918100293, 30.327624485246925],
            [120.13711619158958, 30.32857207778663]
          ]
    const mapTrackDrawRef = ref()
    const drawHandle = () => {
      mapTrackDrawRef.value.drawHandle(trackData)
    }
    const clearDrawHandle = () => {
      mapTrackDrawRef.value.clearDrawHandle()
    }
    const startAnimation = () => {
      mapTrackDrawRef.value.startAnimation()
    }
    const stopAnimation = () => {
      mapTrackDrawRef.value.stopAnimation()
    }
    
    let myMap: any = ref(12)
    let myView: any = ref(null)
    let keyMap: any = ref(Math.random())
    let vectorLayer: any = null
    
    onMounted(() => {
      // console.log('initMap')
    
      nextTick(()=>{
        myMap.value = new Map({
          target: 'map',
          //图层数组 layers
          layers: [
            new TileLayer({
              source: new XYZ({
                crossOrigin: 'anonymous',
                url: 'https://t0.tianditu.gov.cn/DataServer?T=vec_w&x={x}&y={y}&l={z}&tk=37c72a79fe4c6a1b3fa6b1435214b378'
              })
            }),
            new TileLayer({
              source: new XYZ({
                crossOrigin: 'anonymous',
                url: 'https://t0.tianditu.gov.cn/DataServer?T=cva_w&x={x}&y={y}&l={z}&tk=37c72a79fe4c6a1b3fa6b1435214b378'
              })
            })
          ],
          //视图 View
          view: new View({
            projection: "EPSG:4326",
            center: [120.15373797456354, 30.291315691648734],
            zoom: 15,
            maxZoom: 17,
            minZoom: 3,
          }),
          //默认控件
          controls: defaultControls({
            zoom: false,
            rotate: false,
            attribution: false,
          }).extend([
            //添加新控件
            // new MousePosition(),
          ])
        })
        // 获取地图视图
        myView.value = myMap.value.getView()
    
        // myMap.on('singleclick', function (e: any) {
        //   setMarker(e.coordinate)
        // })
    
      })
    })
    
    </script>
    <style scoped lang='less'>
    .map-box {
      width: 100%;
      height: 600px;
      position: relative;
    }
    
    </style>
    
    
    • 效果


      效果

    相关文章

      网友评论

          本文标题:openlayers+vue3 根据坐标集合绘制轨迹

          本文链接:https://www.haomeiwen.com/subject/gjkdmdtx.html