如何通过MapboxGL 实现动态车辆仿真

lxf2023-03-16 08:58:01

功能

首先说一下功能,需要在地图上进行车辆落点,通过颜色来区分车辆类型,可以看到车辆动态变化,点击车辆时可看到点击车辆的一些信息

实现思路

初始化地图

首先import引入mapboxgl且初始化地图,并通过websocket接收数据

注意:Mapbox要先设置accessToken,可以在main.js里设置, 也可以在当前组件引入mapboxgl后设置

let map = null;
import mapboxgl from "mapbox-gl";
/**
 * 初始化地图,并在地图准备后进行websocket连接接收数据
 */
export function initMap() {
  map = new mapboxgl.Map({
    container: "map",
    style: {
      version: 8,
      sources: {
        "wms-imagery": {
          type: "raster",
          tiles: ["/commonmaps/{z}/{x}/{y}.png"]
        }
      },
      layers: [
        {
          id: "blue_map_tgis",
          source: "wms-imagery",
          "source-layer": "blue_map",
          type: "raster"
        }
      ]
    },
    zoom: 12,
    center: [114.05488009848239, 22.542219921599056]
  });
  map.on("load", async function () {
    initWebSocket();
  });
}

数据处理函数及websocket连接

websocket连接拿到数据后,对数据进行处理组合成geojson格式,这里我们把数据处理单独抽出来一个函数

/**
 * 数据处理
 */
function transformGeoJson(data) {
  const geoJSON = {
    type: "FeatureCollection",
    features: []
  };
  data.forEach((item) => {
    let lng = item.lng / 1000000;
    let lat = item.lat / 1000000;
    const geo = {
      type: "Feature",
      geometry: {
        type: "Point",
        coordinates: [lng, lat]
      },
      properties: {
        info1: "信息1",
        info2: "信息2",
        info3: "信息3",
        info4: "信息4",
        info5: "信息5",
      }
    };
    geoJSON.features.push(geo);
  });
  return geoJSON;
}

/**
 * websocket连接
 */
function initWebSocket() {
  websock = new WebSocket(`ws://${location.host}/cardata`);
  websock.onopen = function () {
    console.log("open");
  };
  websock.onmessage = function websocketonmessage(e) {
    const data = JSON.parse(e.data);
    const carData = Object.values(data);
    const geoJSON = transformGeoJson(carData);
    // 判断是否包含了该source,如果包含就通过setData直接更新data,否则第一次就通过addLayer函数添加
    const geojsonSource = map.getSource("carDataSource");
    if (geojsonSource) {
      geojsonSource.setData(geoJSON);
    } else {
      addLayer(geoJSON, "carDataLayer", "carDataSource");
    }
  };
  websock.onclose = function websocketclose() {
    console.log("连接关闭");
  };
}

图层添加

处理完拿到geojson数据,通过上方代码map.getSource判断是否包含了该source,如果包含就通过setData直接更新data,这里就是动态更新数据,否则第一次就通过addLayer函数添加,这里颜色通过match来进行匹配,这里我是随机的两种颜色,也可以根据项目自身需求来进行修改

/**
 * 添加数据源和添加图层
 * @param {*} geoJSON 数据
 * @param {*} layerId 图层ID
 * @param {*} sourceId SOURCEID
 */
function addLayer(geoJSON, layerId, sourceId) {
  map.addSource(sourceId, {
    type: "geojson",
    data: geoJSON
  });
  map.addLayer({
    id: layerId,
    type: "circle",
    minzoom: 6,
    source: sourceId,
    paint: {
      "circle-radius": {
        base: 1.75,
        stops: [
          [12, 2],
          [22, 180]
        ]
      },
      "circle-color": ["match", ["get", "status"], 1, /* 1是绿色 */ "#00DA98", /* 否则红色 */ "#ff0000"]
    }
  });
  changeMapCursor();
  handlerPopup()
}

鼠标手势更改

changeMapCursor函数是实现当鼠标移到我们添加的车辆上面时,鼠标变成手型

/**
 * 改变鼠标手势
 */
function changeMapCursor() {
  const layers = map.getStyle().layers;
  layers.forEach((layer) => {
    const layerId = layer.id;
    if (layerId.includes("carDataLayer")) {
      map.on("mousemove", layerId, function () {
        map.getCanvas().style.cursor = "pointer";
      });
      map.on("mouseleave", layerId, function () {
        map.getCanvas().style.cursor = "";
      });
    }
  });
}

弹框处理函数

handlerPopup函数是处理点击出现弹框信息,你也可以通过点击拿到feature,填写相关的属性信息

/**
 * 弹框函数
 */
function handlerPopup() {
  const popup = new mapboxgl.Popup({
    closeButton: true, // 如果为 true ,弹窗右上角 将出现关闭按钮
    closeOnClick: true // 如果为 true ,点击地图时 弹窗将关闭
  });
  map.on("click", "carSimLayer", (e) => {
    const feature = e.features[0];
    const html = `
          <div class='info'>
            <div class="info-item"><span class="label">车辆信息1:</span>我是信息1</div>
            <div class="info-item"><span class="label">车辆信息2:</span> 我是信息2</div>
            <div class="info-item"><span class="label">车辆信息3:</span>我是信息3</div>
            <div class="info-item"><span class="label">车辆信息4:</span> 我是信息4</div>
            <div class="info-item"><span class="label">车辆信息5:</span>我是信息5</div>
          </div>
        `;
    // 弹框位置直接设置在点击的位置
    popup.setLngLat(feature.geometry.coordinates).setHTML(html).addTo(map);
  });
}

实现效果

到这里基本就已经实现了整个功能,但还有一点不太好的是,更新位置时不是平滑的,是闪烁更新,少了个插值之类的,这个后面再看怎么实现,下面我们看看实现的效果:

如何通过MapboxGL 实现动态车辆仿真

扩展

我这里只是简单的用红绿点表示车辆,大家可以替换成车辆BIM模型,效果会更好