Newer
Older
baseResourceFront / src / views / overview / trail.vue
[wangxitong] on 2 Jul 2021 14 KB 0702车辆总览,轨迹,跟踪
<template>
  <app-container id="overview">
    <!--筛选条件-->
    <div class="search-div">
      <el-form ref="selectForm" :inline="true" :model="listQuery" class="form-container">
        <el-row >
          <el-col style="width: fit-content;">
            <el-date-picker
              v-model="dateTimeRange"
              type="datetimerange"
              range-separator="至"
              size="small"
              value-format="yyyy-MM-dd HH:mm:ss"
              start-placeholder="开始时间"
              end-placeholder="结束时间"
            />
            <!--</el-form-item>-->
            <el-button class="filter-item" type="primary" icon="el-icon-search" size="small" @click="search">查询</el-button>
            <el-button class="filter-item" type="primary" size="small" @click="play">开始</el-button>
            <el-button class="filter-item" type="primary" size="small" @click="pause">暂停</el-button>
            <el-button class="filter-item" type="primary" size="small" @click="stop">停止</el-button>

          </el-col>
          <el-col style="display:flex;width: fit-content;">
            <span style="padding-left: 10px;padding-top: 10px;font-size: 14px">回放速度</span>
            <span style="padding-left:10px;padding-right: 10px;padding-top: 10px;font-size: 14px">快</span>
            <el-slider v-model="sec" :format-tooltip="formatTooltip" :step="10" :min="10" :show-tooltip="false" style="width: 100px;" @change="settimer"/>
            <span style="padding-left: 10px;padding-top: 10px;font-size: 14px;">慢</span>
          </el-col>
          <el-col style="padding-top: 4px;width: fit-content; float: right;">
            <el-button class="filter-item" type="primary" style="margin-left: 10px;background-color: #286090;border: #286090" size="small" @click="staticspeed">时间速度曲线</el-button>
          </el-col>
        </el-row>
        <el-row>
          <el-slider v-model="showindex" :format-tooltip="timeTooltip" :max="list.length!==0?list.length-1:0" :disabled="list.length==0" class="timeslide" @change="changepos"/>
        </el-row>
      </el-form>
    </div>
    <div id="map">
      <div class="namecard" >{{ carName }}</div>
    </div>
    <trail-static v-show="dialogFormVisible" ref="trailStatic" @watchChild="fetchData"/>
  </app-container>
</template>
<script>
  import trailStatic from '@/views/overview/trailStatic'
  import { trail } from '@/api/overview'
  import L from 'leaflet'
  import 'leaflet/dist/leaflet.css'
  import 'leaflet-rotatedmarker/leaflet.rotatedMarker.js'
  export default {
    name: 'Trail',
    components: { trailStatic },
    data() {
      return {
        dialogFormVisible: false, // 是否显示编辑框
        refresh: '10',
        sec: 50,
        showindex: 0,
        state: 0, // 1:开始 2:暂停 3:停止
        showtable: true,
        deptShow: true,
        listQuery: {
          carId: '',
          startTime: '',
          endTime: ''
        }, // 查询条件
        baselayer: [],
        carName: '',
        dateTimeRange: [],
        pathlist: [], // 列表数据
        list: [], // 列表数据
        map: null,
        listLoading: true, // 加载动画
        fullscreenLoading: false, // 全屏加载动画
        timer: null,
        carpoint: null
      }
    },
    watch: {
      dateTimeRange(val) {
        if (val && val.length > 0) {
          this.listQuery.startTime = val[0]
          this.listQuery.endTime = val[1]
        } else {
          this.listQuery.startTime = ''
          this.listQuery.endTime = ''
        }
      }
    },
    mounted() {
      if (this.$route.query) {
        this.listQuery.carId = this.$route.query.id
        this.carName = this.$route.query.name
      }
      const date = new Date()
      const year = date.getFullYear() // 年
      const month = date.getMonth() + 1 // 月
      const day = date.getDate() // 日
      this.dateTimeRange = [year + '-' + month + '-' + day + ' 00:00:00', year + '-' + month + '-' + day + ' 23:59:59']
      this.initMap()
      this.settimer()
    },
    methods: {
      settimer() {
        clearTimeout(this.timer)
        this.timer = setInterval(() => {
          this.showcar()
        }, this.sec * 10)
      },
      staticspeed() {
        this.dialogFormVisible = true
        this.$refs.trailStatic.initDialog(this.dialogFormVisible, this.listQuery, this.carName)
      },
      showcar() {
        if (this.state !== 1) {
          return
        }
        this.showindex++
        if (this.showindex === this.list.length) {
          clearTimeout(this.timer)
          var that = this
          setTimeout(function() { that.stop() }, this.sec * 10)
          return
        }
        this.carpoint.remove()
        // add point
        var Icon = L.icon({
          iconUrl: require('../../assets/global_images/car.png'),
          iconSize: [40, 40]
        })
        var str = '<div style="font-size: 15px;padding: 6px"> <div style="font-weight: bold;padding-bottom: 10px">车辆轨迹点信息</div>' +
          '<div style="padding-bottom: 6px"><label style="font-weight: bold;padding-right: 15px">定位</label>' + this.list[this.showindex].lng + ' , ' + this.list[this.showindex].lat + '</div>' +
          '<div style="padding-bottom: 6px"><label style="font-weight: bold;padding-right: 15px">时间</label>' + this.list[this.showindex].upTime + '</div>' +
          '<div style="padding-bottom: 6px"><label style="font-weight: bold;padding-right: 15px">速度</label>' + this.list[this.showindex].speed + '(km/h)</div>' +
          '</div>'
        var popup = L.popup().setContent(str)
        this.carpoint = L.marker([this.list[this.showindex].lat, this.list[this.showindex].lng], {
          icon: Icon,
          rotationAngle: this.list[this.showindex].direction
        }).addTo(this.map).bindPopup(popup)
        this.carpoint.openPopup()
      },
      play() {
        this.state = 1
        this.settimer()
      },
      pause() {
        this.state = 2
      },
      changepos() {
        if (this.list.length !== 0) {
          if (this.carpoint !== null) {
            this.carpoint.remove()
          }
          var Icon = L.icon({
            iconUrl: require('../../assets/global_images/car.png'),
            iconSize: [40, 40]
          })
          var str = '<div style="font-size: 15px;padding: 6px"> <div style="font-weight: bold;padding-bottom: 10px">车辆轨迹点信息</div>' +
            '<div style="padding-bottom: 6px"><label style="font-weight: bold;padding-right: 15px">定位</label>' + this.list[this.showindex].lng + ' , ' + this.list[this.showindex].lat + '</div>' +
            '<div style="padding-bottom: 6px"><label style="font-weight: bold;padding-right: 15px">时间</label>' + this.list[this.showindex].upTime + '</div>' +
            '<div style="padding-bottom: 6px"><label style="font-weight: bold;padding-right: 15px">速度</label>' + this.list[this.showindex].speed + '(km/h)</div>' +
            '</div>'
          var popup = L.popup().setContent(str)
          this.carpoint = L.marker([this.list[this.showindex].lat, this.list[this.showindex].lng], {
            icon: Icon,
            rotationAngle: Number(this.list[this.showindex].direction)
          }).addTo(this.map).bindPopup(popup)
          this.carpoint.openPopup()
        }
      },
      stop() {
        this.state = 3
        if (this.list.length !== 0) {
          if (this.carpoint !== null) {
            this.carpoint.remove()
          }
          var Icon = L.icon({
            iconUrl: require('../../assets/global_images/car.png'),
            iconSize: [40, 40]
          })
          var str = '<div style="font-size: 15px;padding: 6px"> <div style="font-weight: bold;padding-bottom: 10px">车辆轨迹点信息</div>' +
            '<div style="padding-bottom: 6px"><label style="font-weight: bold;padding-right: 15px">定位</label>' + this.list[0].lng + ' , ' + this.list[0].lat + '</div>' +
            '<div style="padding-bottom: 6px"><label style="font-weight: bold;padding-right: 15px">时间</label>' + this.list[0].upTime + '</div>' +
            '<div style="padding-bottom: 6px"><label style="font-weight: bold;padding-right: 15px">速度</label>' + this.list[0].speed + '(km/h)</div>' +
            '</div>'
          var popup = L.popup().setContent(str)
          this.carpoint = L.marker([this.list[0].lat, this.list[0].lng], {
            icon: Icon,
            rotationAngle: Number(this.list[0].direction)
          }).addTo(this.map).bindPopup(popup)
          this.carpoint.openPopup()
          this.showindex = 0
        }
      },
      formatTooltip(val) {
        return val / 100
      },
      timeTooltip(val) {
        if (this.list.length !== 0) {
          return this.list[val].upTime
        }
      },
      initMap() {
        const map = L.map('map', {
          minZoom: 3,
          maxZoom: 18,
          center: [27.75962, 116.06021],
          zoom: 11,
          zoomControl: false,
          attributionControl: false,
          crs: L.CRS.EPSG3857
        })
        this.map = map // data上需要挂载
        window.map = map
        this.baselayer.push(L.tileLayer(
          'https://t0.tianditu.gov.cn/vec_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=vec&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk=216ee92889e17ab1b083fae665d522b8',
          { subdomains: ['0', '1', '2', '3', '4', '5', '6', '7'] }
        ).addTo(map))
        this.baselayer.push(L.tileLayer(
          'https://t0.tianditu.gov.cn/cva_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=cva&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk=216ee92889e17ab1b083fae665d522b8',
          { subdomains: ['0', '1', '2', '3', '4', '5', '6', '7'] }
        ).addTo(map))
        this.search()
      },
      search() {
        this.fetchData(false)
      },
      setZoom(points) {
        console.log(points)
        if (points.length > 0) {
          var maxLng = points[0][1]
          var minLng = points[0][1]
          var maxLat = points[0][0]
          var minLat = points[0][0]
          var res
          for (var i = points.length - 1; i >= 0; i--) {
            res = points[i]
            if (res[1] > maxLng) maxLng = res[1]
            if (res[1] < minLng) minLng = res[1]
            if (res[0] > maxLat) maxLat = res[0]
            if (res[0] < minLat) minLat = res[0]
          }
          var cenLng = (parseFloat(maxLng) + parseFloat(minLng)) / 2
          var cenLat = (parseFloat(maxLat) + parseFloat(minLat)) / 2
          var zoom = this.getZoom(maxLng, minLng, maxLat, minLat)
          this.map.setView({ lat: cenLat, lng: cenLng }, zoom)
        } else {
          // 没有坐标,显示全中国
          this.map.setView({ lat: 103.388611, lng: 35.563611 }, 5)
        }
      },
      getZoom(maxLng, minLng, maxLat, minLat) {
        var zoom = ['50', '100', '200', '500', '1000', '2000', '5000', '10000', '20000', '25000', '50000', '100000', '200000', '500000', '1000000', '2000000']// 级别18到3。
        var latlng = L.latLng(maxLat, maxLng)
        var distance = latlng.distanceTo(L.latLng(minLat, minLng))
        // var distance = pointA.distanceTo(pointB)// 获取两点距离,保留小数点后两位
        for (var i = 0, zoomLen = zoom.length; i < zoomLen; i++) {
          if (zoom[i] - distance > 0) {
            console.log(i)
            return 18 - i + 2 // 之所以会多2,是因为地图范围常常是比例尺距离的10倍以上。所以级别会增加3。
          }
        }
      },
      setCenter(points) {
        // 获取折线中心点坐标
        if (points.length > 0) {
          let maxLng = points[0][1]
          let minLng = points[0][1]
          let maxLat = points[0][0]
          let minLat = points[0][0]
          let res
          for (let i = points.length - 1; i >= 0; i--) {
            res = points[i]
            if (res[1] > maxLng) maxLng = res[1]
            if (res[1] < minLng) minLng = res[1]
            if (res[0] > maxLat) maxLat = res[0]
            if (res[0] < minLat) minLat = res[0]
          }
          const cenLng = (parseFloat(maxLng) + parseFloat(minLng)) / 2
          const cenLat = (parseFloat(maxLat) + parseFloat(minLat)) / 2

          this.map.setView({ lat: cenLat, lng: cenLng })
        }
      },
      fetchData() { // 划线
        this.listLoading = true
        trail(this.listQuery).then(response => {
          this.list = response.data
          var base = this.baselayer
          this.map.eachLayer(function(layer) {
            if (layer !== base[0] && layer !== base[1]) {
              layer.remove()
            }
          })
          if (this.list.length === 0) {
            this.$message.warning('查询结果为空')
          }
          if (this.list.length !== 0) {
            var myStyle = {
              'color': '#e69010',
              'weight': 5
            }
            this.pathlist = []
            for (var i = 0; i < this.list.length; i++) {
              var item = [Number(this.list[i].lat), Number(this.list[i].lng)]
              this.pathlist.push(item)
            }
            L.polyline(this.pathlist, myStyle).addTo(this.map)
            this.setZoom(this.pathlist)
            var Icon = L.icon({
              iconUrl: require('../../assets/global_images/start.png'),
              iconSize: [40, 40]
            })
            L.marker([this.list[0].lat, this.list[0].lng], {
              icon: Icon
            }).addTo(this.map)
            Icon = L.icon({
              iconUrl: require('../../assets/global_images/end.png'),
              iconSize: [40, 40]
            })
            L.marker([this.list[this.list.length - 1].lat, this.list[this.list.length - 1].lng], {
              icon: Icon
            }).addTo(this.map)
          }
          this.stop()
        })
      }
    }
  }
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
  .search-div{
    padding:12px;
    padding-bottom: 2px; /* 本身输入框有10px下边距*/
    background-color: #fff;
    /*display: -webkit-flex; !* Safari *!*/
    /*display: flex;*/
    justify-content:space-between;
    .search-btn{
      margin-bottom:10px;
    }
  }
  .namecard{
    width: 300px;
    height: 20px;
    z-index: 10000;
    position: absolute;
    left:10px;
    top:10px;
    font-size: 16px;
    font-weight: bold;
    color: #1f2d3d;
  }
  #map {
    border-top: 12px solid #ebebeb;
    width:100%;
    height:67.5vh;
  }
  .my-div-icon {
    width: 10px;
    height: 10px;
    background-color: orange;
    border-radius: 50%;
  }
  .timeslide{
    padding-left: 20px;padding-right: 20px;
    /deep/ .el-slider__button{
      border-radius: 20%;
      width: 40px;
      height: 20px;
    }
    /deep/ .el-slider__button-wrapper{
      width: 40px;
    }
  }
</style>