Unverified Commit ba6f4e20 authored by Sparkf's avatar Sparkf 🏙️ Committed by GitHub
Browse files

Merge pull request #144 from ni1o1/nikebus

bustimer采用新接口
parents d6fc5eb7 656662a8
Loading
Loading
Loading
Loading
+284 −288
Original line number Diff line number Diff line
<template>
  <v-chart class="chart" :option="echartsOption" :autoresize="true" />
  <img src="https://visitor-badge.laobi.icu/badge?page_id=nikebus" alt="visitor badge" class="visitor-badge" />
  <img
    src="https://visitor-badge.laobi.icu/badge?page_id=nikebus"
    alt="visitor badge"
    class="visitor-badge"
  />
</template>

<script>
import axios from 'axios';
import { along, length, nearestPoint, nearestPointOnLine, point, rhumbBearing } from '@turf/turf'
import ECharts from 'vue-echarts'
import axios from "axios";
import {
  along,
  length,
  nearestPoint,
  nearestPointOnLine,
  point,
  rhumbBearing,
} from "@turf/turf";
import ECharts from "vue-echarts";
import { use } from "echarts/core";
// import {CanvasRenderer} from "echarts/renderers";
import { SVGRenderer } from "echarts/renderers";
import {
  ScatterChart,
  LinesChart
} from "echarts/charts";
import { ScatterChart, LinesChart } from "echarts/charts";
import {
  TitleComponent,
  TooltipComponent,
@@ -31,36 +39,15 @@ use([
  // LegendComponent,
  GridComponent,
  ScatterChart,
  LinesChart
  LinesChart,
]);

export default {
  name: 'BusChartVue',
  name: "BusChartVue",
  components: {
    'v-chart': ECharts,
    "v-chart": ECharts,
  },
  data: () => ({
    bus_plate_hash: {
      "8371": { "plate": "粤BDF371", 'route': 1 },//确定
      "8421": { "plate": "粤BDF421", 'route': 1 },//确定
      "8471": { "plate": "粤BDF471", 'route': 1 },//确定
      "8147": { "plate": "粤BDF147", 'route': 1 },//确定
      "8335": { "plate": "粤BDF335", 'route': 1 },//确定
      "8345": { "plate": "粤BDF345", 'route': 1 },//确定
      "8365": { "plate": "粤BDF365", 'route': 1 },//确定
      "8411": { "plate": "粤BDF411", 'route': 1 },//确定
      "8447": { "plate": "粤BDE447", 'route': 1 },//确定
      "18447": { "plate": "粤BDF447", 'route': 1 },//确定
      "8458": { "plate": "粤BDF458", 'route': 1 },//确定
      "8267": { "plate": "粤BDF267", 'route': 1 },
      "8338": { "plate": "粤BDF338", 'route': 1 },
      "8330": { "plate": "粤BDF330", 'route': 1 },
      "298": { "plate": "粤BDF298", 'route': 1 },
      "363": { "plate": "粤BDF363", 'route': 1 },
      "8040": { "plate": "粤BDF040", 'route': 1 },
      "8430": { "plate": "粤BDF430", 'route': 1 },
      "8470": { "plate": "粤BDF470", 'route': 1 }//确定
    },
    lines: [],
    stops1: [],
    stops2: [],
@@ -69,289 +56,297 @@ export default {
      tooltip: {
        show: false,
      },
      title: [{
      title: [
        {
          // text: '南科大校巴实时位置',
        subtext: 'By bilibili@交通数据小旭学长\nGitHub@ni1o1',
        sublink: 'https://github.com/ni1o1/nikebus',
        textAlign: 'right',
        bottom: '2%',
        left: '98%'
      }],
      grid: [{
        top: '10%',
        bottom: '0%',
        left: '26px',
        right: '14px',
      }],
      yAxis: [{
          subtext: "By bilibili@交通数据小旭学长\nGitHub@ni1o1",
          sublink: "https://github.com/ni1o1/nikebus",
          textAlign: "right",
          bottom: "2%",
          left: "98%",
        },
      ],
      grid: [
        {
          top: "10%",
          bottom: "0%",
          left: "26px",
          right: "14px",
        },
      ],
      yAxis: [
        {
          inverse: true,
          min: -100,
          max: 4500,
        type: 'value',
          type: "value",
          boundaryGap: false,
          show: false,
          splitLine: {
          show: false
        }
      }],
      xAxis: [{
        position: 'top',
        verticalAlign: 'top',
            show: false,
          },
        },
      ],
      xAxis: [
        {
          position: "top",
          verticalAlign: "top",
          axisLine: {
          show: false
            show: false,
          },
          axisTick: {
          show: false
            show: false,
          },
          axisLabel: { interval: 0 },
        type: 'category',
        data: ['1号线\n工学院方向',
          '1号线\n欣园方向',
          '2号线\n科研楼方向',
          '2号线\n欣园方向'],
          type: "category",
          data: [
            "1号线\n工学院方向",
            "1号线\n欣园方向",
            "2号线\n科研楼方向",
            "2号线\n欣园方向",
          ],
          splitLine: {
          show: false
        }

      }],
      series: [{
        type: 'scatter',
            show: false,
          },
        },
      ],
      series: [
        {
          type: "scatter",
          label: {
            fontSize: 10,
            show: true,
          color: '#999',
          position: 'right',
          formatter: '{b}'
        },
        data: []
      }, {
        type: 'lines',
        coordinateSystem: 'cartesian2d',
        data: []
      }, {
            color: "#999",
            position: "right",
            formatter: "{b}",
          },
          data: [],
        },
        {
          type: "lines",
          coordinateSystem: "cartesian2d",
          data: [],
        },
        {
          label: {
            fontSize: 11,
            show: true,
          color: '#000',
          textBorderColor: '#fff',
            color: "#000",
            textBorderColor: "#fff",
            textBorderWidth: 2,
          fontWeight: 'bold',
          position: 'right',
            fontWeight: "bold",
            position: "right",
            distance: -5,
          formatter: '{b}'
            formatter: "{b}",
          },
          type: "scatter",
          name: "bus",
          data: [],
        },
        type: 'scatter',
        name: 'bus',
        data: []
      }
      ],
      stateAnimation: {
        duration: 500
      }
        duration: 500,
      },
    },
    timer: {},
    second: 0
    second: 0,
  }),
  methods: {
    setEchartsOption(appendOption) {
      this.echartsOption = appendOption
      this.echartsOption = appendOption;
    },
    //加载线路与站点信息
    load_data() {
      axios.all([
        axios.get('/bus_echart/line1.json'),
        axios.get('/bus_echart/line1_dir2.json'),
        axios.get('/bus_echart/line2.json'),
        axios.get('/bus_echart/stop1.json'),
        axios.get('/bus_echart/stop2.json'),
      axios
        .all([
          axios.get("/bus_echart/line1.json"),
          axios.get("/bus_echart/line2.json"),
          axios.get("/bus_echart/stop1.json"),
          axios.get("/bus_echart/stop2.json"),
        ])
        .then(axios.spread((line1, line1_dir2, line2, stop1, stop2) => {
          const line1data = line1.data
          const line1data2 = line1_dir2.data
          const line2data = line2.data
          const stop1data = stop1.data
          const stop2data = stop2.data
          this.lines = [line1data, line2data, line1data2];
        .then(
          axios.spread((line1,  line2, stop1, stop2) => {
            const line1data = line1.data;
            const line2data = line2.data;
            const stop1data = stop1.data;
            const stop2data = stop2.data;
            this.lines = [line1data, line2data];
            this.stops1 = stop1data;
            this.stops2 = stop2data;
          const line1dir1 = stop1data.features.map(f => {
            const line1dir1 = stop1data.features.map((f) => {
              return {
              value: [1, nearestPointOnLine(line1data['features'][0], f).properties.location * 1000],
                value: [
                  1,
                  nearestPointOnLine(line1data["features"][0], f).properties
                    .location * 1000,
                ],
                name: f.properties.name,
                symbolSize: 8,
              itemStyle: { color: '#ff881b', opacity: 1 }
            }
          })
          const line1dir2 = stop1data.features.map(f => {
                itemStyle: { color: "#ff881b", opacity: 1 },
              };
            });
            const line1dir2 = stop1data.features.map((f) => {
              return {
              value: [0, length(line1data2['features'][0]) * 1000 - nearestPointOnLine(line1data2['features'][0], f).properties.location * 1000],
                value: [
                  0,
                  length(line1data["features"][0]) * 1000 -
                    nearestPointOnLine(line1data["features"][0], f).properties
                      .location *
                      1000,
                ],
                name: f.properties.name,
                symbolSize: 8,
              itemStyle: { color: '#ff881b', opacity: 1 }
            }
          })
          const line2dir1 = stop2data.features.map(f => {
                itemStyle: { color: "#ff881b", opacity: 1 },
              };
            });
            const line2dir1 = stop2data.features.map((f) => {
              return {
              value: [3, nearestPointOnLine(line2data['features'][0], f).properties.location * 1000],
                value: [
                  3,
                  nearestPointOnLine(line2data["features"][0], f).properties
                    .location * 1000,
                ],
                name: f.properties.name,
                symbolSize: 8,
              itemStyle: { color: '#379ff4', opacity: 1 }
            }
          })
          const line2dir2 = stop2data.features.map(f => {
                itemStyle: { color: "#379ff4", opacity: 1 },
              };
            });
            const line2dir2 = stop2data.features.map((f) => {
              return {
              value: [2, length(line2data['features'][0]) * 1000 - nearestPointOnLine(line2data['features'][0], f).properties.location * 1000],
                value: [
                  2,
                  length(line2data["features"][0]) * 1000 -
                    nearestPointOnLine(line2data["features"][0], f).properties
                      .location *
                      1000,
                ],
                name: f.properties.name,
                symbolSize: 8,
              itemStyle: { color: '#379ff4', opacity: 1 }
            }
          })
                itemStyle: { color: "#379ff4", opacity: 1 },
              };
            });
            const echartsOptionAppend = {
            series: [{
              data: [...line1dir1, ...line1dir2, ...line2dir1, ...line2dir2]
            }, {
              series: [
                {
                  data: [
                    ...line1dir1,
                    ...line1dir2,
                    ...line2dir1,
                    ...line2dir2,
                  ],
                },
                {
                  data: [
                    {
                  coords: [[0, 0], [0, length(line1data2['features'][0]) * 1000]],
                  lineStyle: { color: '#ff881b', width: 2, opacity: 1 }
                      coords: [
                        [0, 0],
                        [0, length(line1data["features"][0]) * 1000],
                      ],
                      lineStyle: { color: "#ff881b", width: 2, opacity: 1 },
                    },
                    {
                  coords: [[1, 0], [1, length(line1data['features'][0]) * 1000]],
                  lineStyle: { color: '#ff881b', width: 2, opacity: 1 }
                      coords: [
                        [1, 0],
                        [1, length(line1data["features"][0]) * 1000],
                      ],
                      lineStyle: { color: "#ff881b", width: 2, opacity: 1 },
                    },
                    {
                  coords: [[2, 0], [2, length(line2data['features'][0]) * 1000]],
                  lineStyle: { color: '#379ff4', width: 2, opacity: 1 }
                      coords: [
                        [2, 0],
                        [2, length(line2data["features"][0]) * 1000],
                      ],
                      lineStyle: { color: "#379ff4", width: 2, opacity: 1 },
                    },
                    {
                  coords: [[3, 0], [3, length(line2data['features'][0]) * 1000]],
                  lineStyle: { color: '#379ff4', width: 2, opacity: 1 }
                }
              ]
            }]
          }
          this.setEchartsOption(echartsOptionAppend)
        }));
                      coords: [
                        [3, 0],
                        [3, length(line2data["features"][0]) * 1000],
                      ],
                      lineStyle: { color: "#379ff4", width: 2, opacity: 1 },
                    },
                  ],
                },
              ],
            };
            this.setEchartsOption(echartsOptionAppend);
          })
        );
    },
    updateBusPos() {
      if (this.lines.length > 0) {
        axios.get('https://bus.sustcra.com/api/v2/monitor_osm/').then(response => {
          const res = response.data
          const busData = res.filter(f => f.it == 0).map(f => {
        axios
          .get("https://bus.sustcra.com/api/v2/monitor_osm/")
          .then((response) => {
            const res = response.data;
            const busData = res
              .filter((f) => (f.is_operating == 1))
              .map((f) => {
                //哪条线路
            let thisRoute = 0
            if (f.route_code == 202) {
              thisRoute = 1
                let thisRoute = 0;
                if (f.route_code == "XYBS2") {
                  thisRoute = 1;
                } else if (f.route_code == "XYBS1") {
                  thisRoute = 0;
                }
            //判断是在哪个方向上
            const mcp = point([f.lng, f.lat])
            let thisLine = this.lines[thisRoute]['features'][0]

                //线上最近点
            let p_nearest = nearestPointOnLine(thisLine, mcp)
            let p_nearest_loc = p_nearest.properties.location
            //线上最近点+1米处的点
            const p_next = along(thisLine, p_nearest_loc + 0.0001);
            //计算切线角度
            let bearing = rhumbBearing(p_nearest, p_next);
            if (bearing < 0) {
              bearing += 360
                const mcp = point([f.lng, f.lat]);
                let thisLine = this.lines[thisRoute]["features"][0];
                let p_nearest = nearestPointOnLine(thisLine, mcp);
                let p_nearest_loc = p_nearest.properties.location;

                let dist = p_nearest_loc * 1000
                if (f.route_dir>0){
                  dist = length(thisLine) * 1000 - p_nearest_loc * 1000;
                }
            //通过车辆方向角判断车辆行进方向
            let route_dir = 2
            const angle = parseInt(f.course) - parseInt(bearing)
            if ((angle < 20) && (angle > -20)) {
              route_dir = 1
                
                //如果距离大于50米,则不显示
                if ((p_nearest.properties.dist * 1000 > 50)|(dist<50)) {
                  return {
                value: [route_dir + thisRoute * 2, p_nearest_loc * 1000],
                name: this.bus_plate_hash[f.id].plate, itemStyle: { color: '#222' },
                symbol: 'image://https://bus.sustcra.com/bus-top-view.png',
                    value: [-1, 0],
                    name: f.id,
                    itemStyle: { color: "#222", show: false },
                    symbol: "image://https://bus.sustcra.com/bus-top-view.png",
                    symbolSize: 30,
                    symbolRotate: 180,
                speed: f.speed
              }
            } else if ((angle < -160) || (angle > 160)) {
              route_dir = 0
              let dist = length(thisLine) * 1000 - p_nearest_loc * 1000
              //如果是线路1的另一个方向,则重新计算距离
              if (thisRoute == 0) {
                thisLine = this.lines[2]['features'][0]
                p_nearest = nearestPointOnLine(thisLine, mcp)
                p_nearest_loc = p_nearest.properties.location
                dist = length(thisLine) * 1000 - p_nearest_loc * 1000
                    speed: f.speed,
                  };
                }
                return {
                value: [route_dir + thisRoute * 2, dist],
                name: this.bus_plate_hash[f.id].plate, itemStyle: { color: '#222' },
                symbol: 'image://https://bus.sustcra.com/bus-top-view.png',
                  value: [1- f.route_dir + thisRoute * 2, dist],
                  name: f.id,
                  itemStyle: { color: "#222" },
                  symbol: "image://https://bus.sustcra.com/bus-top-view.png",
                  symbolSize: 30,
                  symbolRotate: 180,
                speed: f.speed
              }
            } else {
              //从历史信息里找到这辆车的信息
              if (this.historyBusData.length > 0) {
                const historyBusData_this = this.historyBusData.filter(p => {
                  if (typeof (p) != 'undefined') {
                    return p.name == this.bus_plate_hash[f.id].plate
                  }
                }
                )
                if (historyBusData_this.length > 0) {
                  return historyBusData_this[0]
                }
              }
            }
          }
          )
          this.historyBusData = busData.filter(p => typeof (p) != 'undefined')
          const echartsOptionAppend = { series: [{}, {}, { data: busData }] }
          this.setEchartsOption(echartsOptionAppend)
        })
                  speed: f.speed,
                };
              });
            this.historyBusData = busData.filter(
              (p) => typeof p != "undefined"
            );
            const echartsOptionAppend = { series: [{}, {}, { data: busData }] };
            this.setEchartsOption(echartsOptionAppend);
          });
      }
    },
    refresh() {
      this.second += 1
      this.second += 1;
      //5秒校准一次正确位置
      if (this.second % 10 === 0) {
        this.updateBusPos()
        this.updateBusPos();
      }else{
        //0.5秒通过速度推测一次车辆的位置
        const newdata = this.historyBusData.map(f => {
          if (typeof (f) != 'undefined') {
            let routedir = 2
            if (f.value[0] == 1) {
              routedir = 0
            }
            if (f.value[0] == 2) {
              routedir = 1
            }
            if (f.value[0] == 3) {
              routedir = 1
            }
            if (f.value[0] == 0) {
              routedir = 2
            }
            let next_dist = f.value[1] + f.speed * 0.5 * 1000 / 7200
            if (next_dist > length(this.lines[routedir]['features'][0]) * 1000
            ) {
              next_dist = length(this.lines[routedir]['features'][0]) * 1000
            }
            return { ...f, value: [f.value[0], next_dist] }
          }
        })
        this.historyBusData = newdata.filter(p => typeof (p) != 'undefined');
        this.setEchartsOption({ series: [{}, {}, { data: newdata }] })
        //获取定位
       if (navigator.geolocation) {
         navigator.geolocation.getCurrentPosition(pos => {
            let lat = pos.coords.latitude,
              lng = pos.coords.longitude;
            let lat = pos.coords.latitude
            let lng = pos.coords.longitude
   
            //标记出最近的站点
            const p = point([lng, lat])
            const nearest_line1 = nearestPoint(p, this.stops1);
            const line1_pos_dir1 = nearestPointOnLine(this.lines[0]['features'][0], nearest_line1).properties.location * 1000
            const line1_pos_dir2 = length(this.lines[2]['features'][0]) * 1000 - nearestPointOnLine(this.lines[2]['features'][0], nearest_line1).properties.location * 1000
            const line1_pos_dir2 = length(this.lines[0]['features'][0]) * 1000 - nearestPointOnLine(this.lines[0]['features'][0], nearest_line1).properties.location * 1000
            const nearest_line2 = nearestPoint(p, this.stops2);

            const line2_pos_dir1 = nearestPointOnLine(this.lines[1]['features'][0], nearest_line2).properties.location * 1000
@@ -418,21 +413,22 @@ export default {
          })
        }
      }
    }
    },
  },
  watch: {
    lines: 'updateBusPos'
    lines: "updateBusPos",
  },
  mounted() {
    this.load_data();
    this.updateBusPos();
    this.timer = setInterval(() => {
      this.refresh();
    }, 500); //0.5秒刷新一次位置,每10次刷新中,9次为推测的位置,1次为抓取的位置
  },
  unmounted() {
    clearInterval(this.timer);
  }
}
  },
};
</script>

<style scoped>