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

  • 绘制组件 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>

  • 效果


    效果
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,504评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,434评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,089评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,378评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,472评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,506评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,519评论 3 413
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,292评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,738评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,022评论 2 329
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,194评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,873评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,536评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,162评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,413评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,075评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,080评论 2 352

推荐阅读更多精彩内容