GIS mapbox地图

0 阅读6分钟

gis_map1.png

gis_map2.png

  1. mapbox GL javascript库
  2. 使用天地图作为瓦片
<template>
  <div id="cxtjmap"></div>
</template>

<script setup lang="ts">
import circleImg from '@/images/circle.png';
import minusImg from '@/images/minus.png';
import plusImg from '@/images/plus.png';
import thunderImg from '@/images/thunder.png';
import thunderstormImg from '@/images/thunderstorm.png';
import triangleImg from '@/images/triangle.png';
import mapboxgl from 'mapbox-gl';
import { onBeforeUnmount, onMounted } from 'vue';

let map: mapboxgl.Map;
function initMapBox() {
  mapboxgl.accessToken ='';
  const params:any = {
    container: 'cxtjmap',
    style: {
      sprite: 'mapbox://sprites/examples/cjikt35x83t1z2rnxpdmjs7y7',
      glyphs: 'mapbox://fonts/mapbox/{fontstack}/{range}.pbf',
      version: 8,
      sources: {
        tdtVec: {
          type: 'raster',
          tiles: [
            'http://t0.tianditu.gov.cn/vec_w/wmts?service=WMTS&request=GetTile&version=1.0.0&tilematrixset=w&layer=vec&style=default&tilematrix={z}&tilerow={y}&tilecol={x}&format=tiles&tk=9f1439233cfad9ed75a0f55417a79e42',
            'http://t1.tianditu.gov.cn/vec_w/wmts?service=WMTS&request=GetTile&version=1.0.0&tilematrixset=w&layer=vec&style=default&tilematrix={z}&tilerow={y}&tilecol={x}&format=tiles&tk=9f1439233cfad9ed75a0f55417a79e42',
            'http://t2.tianditu.gov.cn/vec_w/wmts?service=WMTS&request=GetTile&version=1.0.0&tilematrixset=w&layer=vec&style=default&tilematrix={z}&tilerow={y}&tilecol={x}&format=tiles&tk=9f1439233cfad9ed75a0f55417a79e42',
            'http://t3.tianditu.gov.cn/vec_w/wmts?service=WMTS&request=GetTile&version=1.0.0&tilematrixset=w&layer=vec&style=default&tilematrix={z}&tilerow={y}&tilecol={x}&format=tiles&tk=9f1439233cfad9ed75a0f55417a79e42',
          ],
          tileSize: 256,
        },
        tdtCva: {
          type: 'raster',
          tiles: [
            'http://t0.tianditu.gov.cn/cva_w/wmts?service=WMTS&request=GetTile&version=1.0.0&tilematrixset=w&layer=cva&style=default&tilematrix={z}&tilerow={y}&tilecol={x}&format=tiles&tk=9f1439233cfad9ed75a0f55417a79e42',
            'http://t1.tianditu.gov.cn/cva_w/wmts?service=WMTS&request=GetTile&version=1.0.0&tilematrixset=w&layer=cva&style=default&tilematrix={z}&tilerow={y}&tilecol={x}&format=tiles&tk=9f1439233cfad9ed75a0f55417a79e42',
            'http://t2.tianditu.gov.cn/cva_w/wmts?service=WMTS&request=GetTile&version=1.0.0&tilematrixset=w&layer=cva&style=default&tilematrix={z}&tilerow={y}&tilecol={x}&format=tiles&tk=9f1439233cfad9ed75a0f55417a79e42',
            'http://t3.tianditu.gov.cn/cva_w/wmts?service=WMTS&request=GetTile&version=1.0.0&tilematrixset=w&layer=cva&style=default&tilematrix={z}&tilerow={y}&tilecol={x}&format=tiles&tk=9f1439233cfad9ed75a0f55417a79e42',
          ],
          tileSize: 256,
        },
      },
      layers: [
        {
          id: 'background',
          type: 'background',
          minzoom: 0,
          maxzoom: 24,
          layout: { visibility: 'visible' },
          paint: { 'background-color': 'rgba(11, 15, 79, 1)' },
        },
        {
          id: 'tdtVec',
          type: 'raster',
          source: 'tdtVec',
        },
        {
          id: 'tdtCva',
          type: 'raster',
          source: 'tdtCva',
        },
      ],
    },
    center: { lng: 170, lat: 66 },
    zoom: 2,
    maxBounds: [
      [50, 2],
      [166, 56],
    ],
    pitch: 0,
    bearing: 0,
    logoPosition: undefined,
    antialias: true,
  };

  map = new mapboxgl.Map(params);
}
function initImage() {
  loadImage(minusImg, 'minus', { sdf: true });
  loadImage(triangleImg, 'triangle', { sdf: true });
  loadImage(plusImg, 'plus', { sdf: true });
  loadImage(circleImg, 'circle', { sdf: true });
  loadImage(thunderImg, 'thunder', { sdf: true });
  loadImage(thunderstormImg, 'thunderstorm', { sdf: true });
}
function loadImage(src: string, imageName: string, opt?: any) {
  map.loadImage(
    src,
    (error?: Error | undefined, image?: HTMLImageElement | ImageBitmap | undefined) => {
      if (error) throw error;
      map.addImage(imageName, image!, opt);
    },
  );
}
function initThunder() {
  map.addSource('intracloud-lightning', {
    type: 'geojson',
    data: {
      type: 'FeatureCollection',
      features: [],
    },
  });
  map.addLayer({
    id: 'intracloud-lightning',
    type: 'circle',
    source: 'intracloud-lightning',
    paint: {
      'circle-color': ['get', 'color'],
      'circle-radius': ['interpolate', ['linear'], ['zoom'], 0, 3, 10, 5],
      'circle-stroke-width': 0,
    },
  });
  map.addSource('ground-lightning', {
    type: 'geojson',
    data: {
      type: 'FeatureCollection',
      features: [],
    },
  });
  map.addLayer({
    id: 'ground-lightning',
    type: 'symbol',
    source: 'ground-lightning',
    layout: {
      'icon-image': ['get', 'type'],
      'icon-size': [
        'match',
        ['get', 'size'],
        'medium',
        0.3,
        'large',
        0.5,
        0.3, // 默认大小
      ],
      'icon-rotation-alignment': 'map',
      'icon-allow-overlap': true,
    },
    paint: {
      'icon-color': 'green',
    },
  });
  initLineLayer(map);
  bindEvent();
}
// 初始化
function initLineLayer(_map: mapboxgl.Map) {
  _map.addSource('location-line', {
    type: 'geojson',
    data: {
      type: 'FeatureCollection',
      features: [],
    },
  });

  _map.addLayer({
    id: 'location-line',
    type: 'line',
    source: 'location-line',
    layout: {
      'line-join': 'round',
      'line-cap': 'round',
    },
    paint: {
      'line-color': 'red',
      'line-width': 1,
    },
  });
  _map.addSource('location-line-inner', {
    type: 'geojson',
    data: {
      type: 'FeatureCollection',
      features: [],
    },
  });
  _map.addLayer({
    id: 'location-line-inner',
    type: 'line',
    source: 'location-line-inner',
    layout: {
      'line-join': 'round',
      'line-cap': 'round',
    },
    paint: {
      'line-color': 'red',
      'line-width': 1,
      'line-dasharray': [10, 5],
    },
  });
  _map.addSource('location-line-text', {
    type: 'geojson',
    data: {
      type: 'FeatureCollection',
      features: [],
    },
  });
  //添加文本图层
  _map.addLayer({
    id: 'location-line-text',
    type: 'symbol',
    source: 'location-line-text',
    layout: {
      'text-field': ['get','title'], // 文本来源
      'text-offset': [0, 0.5], // 文本偏移量
      'text-anchor': 'bottom-left', // 文本锚点
      'text-size': 10, // 文本大小
      'text-allow-overlap': true,
      'text-ignore-placement': true,
    },
    paint: {
      'text-color': 'green', // 文本颜色
    },
  });
}
function bindEvent() {
  // 鼠标样式
  let popupPoint: any = null;
  const popup = new mapboxgl.Popup({
    closeOnClick: false,
    closeButton: false,
    offset: 26,
    maxWidth: '340px',
    className: 'thunder-popup',
  });

  map.on('mouseleave', 'ground-lightning', function () {
    map.getCanvas().style.cursor = 'auto';
    popup.remove();
    timeHover = true;
  });

  map.on('mouseleave', 'intracloud-lightning', function () {
    map.getCanvas().style.cursor = 'auto';
    popup.remove();
    timeHover = true;
  });

  map.on('mousemove', 'ground-lightning', async function (e) {
    map.getCanvas().style.cursor = 'pointer';
    if (popupPoint?.id != e.features![0].properties!.id) {
      popupPoint = e.features![0].properties;
    }
    console.log(getPopupStr(popupPoint));
    popup
      .setLngLat([e.lngLat.lng, e.lngLat.lat])
      .setHTML((await getPopupStr(popupPoint)) as string)
      .addTo(map);
  });
  map.on('mousemove', 'intracloud-lightning', async function (e) {
    map.getCanvas().style.cursor = 'pointer';
    if (popupPoint?.id != e.features![0].properties!.id) {
      popupPoint = e.features![0].properties;
    }
    popup
      .setLngLat([e.lngLat.lng, e.lngLat.lat])
      .setHTML((getPopupStr(popupPoint)) as string)
      .addTo(map);
  });

  map.on('click', 'ground-lightning', async function (e) {
    map.getCanvas().style.cursor = 'pointer';
    if (popupPoint?.id != e.features![0].properties!.id) {
      popupPoint = e.features![0].properties;
    }
    
    const resData = getLocationLineData(popupPoint);

    //@ts-ignore
    map.getSource('location-line-inner').setData({
      type: 'FeatureCollection',
      features: resData.inner,
    });
    //@ts-ignore
    map.getSource('location-line').setData({
      type: 'FeatureCollection',
      features: resData.outside,
    });
    //@ts-ignore
    map.getSource('location-line-text').setData({
      type: 'FeatureCollection',
      features: resData.stationName,
    });
  });
}
function convexHull(points: { lat: number; lon: number }[]): { lat: number; lon: number }[] {
  // Find the point with the lowest latitude (and smallest longitude if latitudes are equal)
  let start = points.reduce(
    (prev, curr) =>
      prev.lat < curr.lat
        ? prev
        : prev.lat === curr.lat
          ? prev.lon < curr.lon
            ? prev
            : curr
          : curr,
    points[0],
  );

  // Sort points by polar angle
  points.sort((a, b) => polarAngle(start, a) - polarAngle(start, b));

  // Remove the starting point from the sorted list
  const stack: { lat: number; lon: number }[] = [start, points[0]];

  for (let i = 1; i < points.length; i++) {
    while (
      stack.length >= 2 &&
      orientation(stack[stack.length - 2], stack[stack.length - 1], points[i]) <= 0
    ) {
      stack.pop();
    }
    stack.push(points[i]);
  }

  return stack;
}
function polarAngle(
  pivot: { lat: number; lon: number },
  point: { lat: number; lon: number },
): number {
  return Math.atan2(point.lat - pivot.lat, point.lon - pivot.lon);
}
function orientation(
  p1: { lat: number; lon: number },
  p2: { lat: number; lon: number },
  p3: { lat: number; lon: number },
): number {
  return (p2.lon - p1.lon) * (p3.lat - p1.lat) - (p2.lat - p1.lat) * (p3.lon - p1.lon);
}
function calculateDistance(lat1: number, lon1: number, lat2: number, lon2: number) {
  const R = 6371; // 地球半径,单位:km
  const dLat = ((lat2 - lat1) * Math.PI) / 180;
  const dLon = ((lon2 - lon1) * Math.PI) / 180;
  const a =
    Math.sin(dLat / 2) * Math.sin(dLat / 2) +
    Math.cos((lat1 * Math.PI) / 180) *
      Math.cos((lat2 * Math.PI) / 180) *
      Math.sin(dLon / 2) *
      Math.sin(dLon / 2);
  const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
  const distance = R * c;
  return distance;
}

let timeHover = true;
let strBox: string | null = null;
function getPopupStr(PointObj: any) {
  if (timeHover) {
    timeHover = false;
    if (PointObj) {
      console.log(PointObj);
      strBox = `<div class="thunder">
      <div class="item">地址:${PointObj.address}</div>
      </div>`;
    } else {
      strBox = `<div class="thunder">
       <div>无数据 </div>
       </div>`;
    }
  }

  return strBox;
}
function getLocationLineData(currentPoint: any) {
  
  let stationList = JSON.parse(currentPoint.stationList);

  let stationName: any[] = [];

  let innerLine: any[] = [];
  for (let j = 0; j < stationList.length; j++) {
    // 以下为 地图上画站点名称
    let locationText = {
      type: 'Feature',
      geometry: {
        type: 'Point',
        coordinates: [stationList[j].lon, stationList[j].lat], // 文本的经纬度
      },
      properties: {
        title: stationList[j].sta_name_new,
      },
    };
    stationName.push(locationText);

    // 以下为定位点和站之间划线
    let data2 = {
      type: 'Feature',
      properties: {},
      geometry: {
        type: 'LineString',
        coordinates: new Array(),
      },
    };
    data2.geometry.coordinates.push([currentPoint.longitude, currentPoint.latitude]);
    data2.geometry.coordinates.push([stationList[j].lon, stationList[j].lat]);
    innerLine.push(data2);

    // 以下为定位定位點和站之间画公里数
    const distance = calculateDistance(
      currentPoint.latitude,
      currentPoint.longitude,
      stationList[j].lat,
      stationList[j].lon,
    );
    let lon: number = (currentPoint.longitude + stationList[j].lon) / 2;
    let lat: number = (currentPoint.latitude + stationList[j].lat) / 2;
    let locationText2 = {
      type: 'Feature',
      geometry: {
        type: 'Point',
        coordinates: [lon, lat], // 文本的经纬度
      },
      properties: {
        title: Math.floor(distance),
      },
    };
    stationName.push(locationText2);
  }

  let data1 = {
    type: 'Feature',
    properties: {},
    geometry: {
      type: 'LineString',
      coordinates: new Array(),
    },
  };

  let locationToLocationLine: any[] = [];

  if (stationList.length > 2) {
    const sortedCoordinates = convexHull(stationList);

    for (let j = 0; j < sortedCoordinates.length; j++) {
      data1.geometry.coordinates.push([sortedCoordinates[j].lon, sortedCoordinates[j].lat]);

      let k = j + 1;
      if (j == sortedCoordinates.length - 1) {
        data1.geometry.coordinates.push([sortedCoordinates[0].lon, sortedCoordinates[0].lat]);
        k = 0;
      }
      const distance = calculateDistance(
        stationList[j].lat,
        stationList[j].lon,
        stationList[k].lat,
        stationList[k].lon,
      );
      let lon: number = (stationList[j].lon + stationList[k].lon) / 2;
      let lat: number = (stationList[j].lat + stationList[k].lat) / 2;
      let distanceText = {
        type: 'Feature',
        geometry: {
          type: 'Point',
          coordinates: [lon, lat], // 文本的经纬度
        },
        properties: {
          title: Math.floor(distance),
        },
      };
      stationName.push(distanceText);
    }
    locationToLocationLine.push(data1);
  }

  return { inner: innerLine, outside: locationToLocationLine, stationName: stationName };
}

function drawMapData() {
  let data = formMapData();
  updateLocationThunder(data.groundLightingDataArr, data.cloudLightingDataArr);
}
function updateLocationThunder(groundLightingDataArr: any, cloudLightingDataArr: any) {
  //@ts-ignore
  map.getSource('ground-lightning').setData({
    type: 'FeatureCollection',
    features: groundLightingDataArr,
  });
  //@ts-ignore
  map.getSource('intracloud-lightning').setData({
    type: 'FeatureCollection',
    features: cloudLightingDataArr,
  });
}
function formMapData() {
  const stationListData = [
    {
        "town": "陈巴尔虎旗",
        "sta_name": "站1",
        "city": "呼伦贝尔市",
        "sta_name_new": "陈巴尔虎旗",
        "lon": 119.4489,
        "province": "内蒙古自治区",
        "lat": 49.3392,
    },
    {
        "town": "新巴尔虎左旗",
        "sta_name": "站2",
        "city": "呼伦贝尔市",
        "sta_name_new": "新巴尔虎左旗",
        "lon": 118.2631,
        "province": "内蒙古自治区",
        "lat": 48.1864,
    },
    {
        "town": "新巴尔虎右旗",
        "sta_name": "站3",
        "city": "呼伦贝尔市",
        "sta_name_new": "新巴尔虎右旗",
        "lon": 116.8103,
        "province": "内蒙古自治区",
        "lat": 48.6781,
    }
  ]
  let mapData = [{logotype:1,longitude:111.654,latitude:40.812,address:'内蒙古自治区呼和浩特市赛罕区大学东街126号',stationList:stationListData}];
  const groundLightingDataArr: any = [];
  const cloudLightingDataArr: any = [];
  mapData.forEach((el: any, i: number) => {
    //最后一条算最新数据
    if (i === mapData.length - 1) {
      groundLightingDataArr.push({
        type: 'Feature',
        properties: {
          id: i,
          color: '#fff',
          name: 'new',
          type: 'thunder',
          size: 'large',
          ...el,
        },
        geometry: {
          type: 'Point',
          coordinates: [el.longitude, el.latitude],
        },
      });
    } else {
      if (el.lightningType == 0) {//云闪
        cloudLightingDataArr.push({
          type: 'Feature',
          properties: {
            color: 'green',
            id: i,
            ...el,
          },
          geometry: {
            type: 'Point',
            coordinates: [el.longitude, el.latitude],
          },
        });
      }
      if (el.lightningType == 1) {//地闪
        if (el.peakCurrentIntensity > 0) {//正地闪
          groundLightingDataArr.push({
            type: 'Feature',
            properties: {
              id: i,
              color: 'red',
              name: '加',
              type: 'plus',
              size: 0.3,
              ...el,
            },
            geometry: {
              type: 'Point',
              coordinates: [el.longitude, el.latitude],
            },
          });
        }
        if (el.peakCurrentIntensity <= 0) {//负地闪
          groundLightingDataArr.push({
            type: 'Feature',
            properties: {
              id: i,
              color: 'green',
              name: '减',
              type: 'minus',
              size: 0.3,
              ...el,
            },
            geometry: {
              type: 'Point',
              coordinates: [el.longitude, el.latitude],
            },
          });
        }
       
      }
    }
  });
  return {'groundLightingDataArr':groundLightingDataArr,'cloudLightingDataArr':cloudLightingDataArr};
}
function loadMap() {
  map.on('load', function () {
    initImage();
    initThunder();
    drawMapData();
  });
}
function destroyMapbox() {
  map && map.remove();
}

onMounted(() => {
  initMapBox();
  loadMap();
});
onBeforeUnmount(() => {
  destroyMapbox();
});
</script>

<style>
#cxtjmap {
  height: 100%;
}
.thunder-popup {
  .thunder {
    display: flex;
    flex-wrap: wrap;
    color: black;
    font-size: 14px;
    margin: 12px;
    div {
      width: 50%;
      margin-bottom: 6px;

      &.w-100 {
        width: 100%;
      }
    }
  }
}
</style>

完整代码地址 github