leaflet实践

// ParentComponent
<script setup lang="ts">
import {onMounted} from 'vue'
import L from "leaflet";
import 'leaflet/dist/leaflet.css'
import { MarkerClusterGroup } from "leaflet.markercluster" // 标记聚类
import 'leaflet.markercluster/dist/MarkerCluster.css';
import 'leaflet.markercluster/dist/MarkerCluster.Default.css';
import 'leaflet.heat';

onMounted(()=> {
  //  在 Leaflet 的瓦片图层 URL 模板中,{s} 是一个占位符,用于表示瓦片服务的子域名(subdomain)。这是一种常见的负载均衡技术,地图服务提供商通过多个子域名来分散请求,提高并发性能和稳定性
  const tdtKey = ''; // 天地图key
  // 初始化地图
  const map = L.map('map').setView([39.9, 116.4], 17); // 设置中心点为北京,缩放级别17
  const subdomains = ['t0','t1','t2','t3','t4','t5','t6','t7']; // 共8个子域
  const vecLayer = L.tileLayer(
    `http://{s}.tianditu.gov.cn/vec_w/wmts?tk=${tdtKey}&service=wmts&request=GetTile&version=1.0.0&layer=vec&tileMatrixSet=w&format=tiles&tileMatrix={z}&tileRow={y}&tileCol={x}`,
    {
      subdomains,
      maxZoom: 18,
      minZoom: 1,
      attribution: '© <a href="https://www.openstreetmap.org/copyright">Hello World</a>'
    }
  ).addTo(map);

  // coordinates跟leaflet的坐标是反的 不过会自动转换
  // Feature 点
  const pointGeoJSON = {
    "type": "Feature",
    "geometry": {
      "type": "Point",
      "coordinates": [116.4, 39.9] // [经度, 纬度]
    },
    "properties": {
      "name": "Beijing"
    }
  };

  L.geoJSON(pointGeoJSON, {
    pointToLayer: (feature, latlng) => {
      return L.circleMarker(latlng, {
        radius: 66,
        fillColor: "red",
        color: "#000",
        weight: 1
      });
    }
  }).addTo(map);

  // LineString 线
  const lineGeoJSON = {
    "type": "Feature",
    "geometry": {
      "type": "LineString",
      "coordinates": [
        [116.3, 39.8], // 起点
        [116.4, 39.9], // 途经点
        [116.5, 40.0]  // 终点
      ]
    },
    "properties": {
      "name": "Road ABC"
    }
  };

  L.geoJSON(lineGeoJSON, {
    style: {
      color: "blue",
      weight: 5,
      opacity: 0.7
    }
  }).addTo(map);

  // Polygon 多边形
  const polygonGeoJSON = {
    "type": "Feature",
    "geometry": {
      "type": "Polygon",
      "coordinates": [
        [ // 外轮廓(必须闭合)
          [116.3, 39.8],
          [116.5, 39.8],
          [116.5, 40.0],
          [116.3, 40.0],
          [116.3, 39.8] // 首尾相同,闭合多边形
        ],
        [ // 可选:内洞(如岛屿)
          [116.35, 39.85],
          [116.45, 39.85],
          [116.45, 39.95],
          [116.35, 39.95],
          [116.35, 39.85]
        ]
      ]
    },
    "properties": {
      "name": "A Lake"
    }
  };

  L.geoJSON(polygonGeoJSON, {
    style: {
      fillColor: "green",
      fillOpacity: 0.5,
      color: "darkgreen"
    }
  }).addTo(map);

  // MultiPoint 多点
  const multiPointGeoJSON = {
    "type": "Feature",
    "geometry": {
      "type": "MultiPoint",
      "coordinates": [
        [116.4, 39.9],
        [116.41, 39.91]
      ]
    },
    "properties": {
      "name": "Cities"
    }
  };

  L.geoJSON(multiPointGeoJSON, {
    pointToLayer: (feature, latlng) => {
      return L.circleMarker(latlng, { radius: 5 });
    }
  }).addTo(map);

  // MultiLineString 多线
  const multiLineGeoJSON = {
    "type": "Feature",
    "geometry": {
      "type": "MultiLineString",
      "coordinates": [
        [[116.3, 39.8], [116.4, 39.9]], // 第一条线
        [[116.4, 39.9], [116.5, 40.0]]  // 第二条线
      ]
    }
  };

  // MultiPolygon 多面
  const multiPolygonGeoJSON = {
    "type": "Feature",
    "geometry": {
      "type": "MultiPolygon",
      "coordinates": [
        [ // 第一个多边形
          [[116.3, 39.8], [116.5, 39.8], [116.5, 40.0], [116.3, 39.8]]
        ],
        [ // 第二个多边形
          [[116.6, 39.6], [116.7, 39.6], [116.7, 39.7], [116.6, 39.6]]
        ]
      ]
    }
  };
  L.geoJSON(multiPolygonGeoJSON, {
    style: { fillColor: "purple" }
  }).addTo(map);
  L.geoJSON(multiLineGeoJSON, {
    style: { color: "orange" }
  }).addTo(map);

  // GeometryCollection 几何集合
  const collectionGeoJSON = {
    "type": "Feature",
    "geometry": {
      "type": "GeometryCollection",
      "geometries": [
        { "type": "Point", "coordinates": [116.4, 39.9] },
        { "type": "LineString", "coordinates": [[116.3, 39.8], [116.5, 40.0]] }
      ]
    },
    properties: {
      name: '12121212'
    }
  };
  L.geoJSON(collectionGeoJSON).addTo(map);

  // 添加自定义图标标记
  const govIcon = L.icon({
    iconUrl: '/icons/government.png',
    iconSize: [36, 40],
    popupAnchor: [0, -15]
  });

  const marker = L.marker([31.2304, 121.4737], {
    icon: govIcon,
    title: '上海市人民政府'
  }).addTo(map);

  marker.bindPopup(`
  <div class="custom-popup">
    <h3>上海市人民政府</h3>
    <p>地址:人民大道200号</p>
  </div>
`).openPopup();


  // 标记聚类
  const poiCluster = new MarkerClusterGroup();
  // 模拟加载POI数据
  const poiData = [
    {name: "东方明珠", latlng: [31.2398, 121.4997], type: "landmark"},
    {name: "豫园", latlng: [31.2269, 121.4904], type: "park"}
  ];

  poiData.forEach(poi => {
    const marker = L.marker(poi.latlng)
      .bindPopup(poi.name);

    poiCluster.addLayer(marker);
  });
  map.addLayer(poiCluster);

  // 绘制业务区域
  const businessArea = L.polygon([
    [31.235, 121.460],
    [31.235, 121.490],
    [31.215, 121.490],
    [31.215, 121.460]
  ], {
    color: '#3388ff',
    fillColor: '#3388ff',
    fillOpacity: 0.3
  }).addTo(map);
  businessArea.bindPopup("核心商务区");

  // 热力图
  // 生成模拟热力点
  const heatPoints = [];
  for(let i=0; i<500; i++) {
    const lat = 31.22 + Math.random() * 0.1;
    const lng = 121.46 + Math.random() * 0.1;
    heatPoints.push([lat, lng, Math.random()]);
  }
 // 添加热力图层
  const heatLayer = L.heatLayer(heatPoints, {
    radius: 25,
    blur: 15,
    gradient: {0.4: 'blue', 0.65: 'lime', 1: 'red'}
  }).addTo(map);


  // 车辆图标
  const busIcon = L.icon({
    iconUrl: 'bus.png',
    iconSize: [32, 32],
    iconAnchor: [16, 16]
  });

// 轨迹线路
  const route = L.polyline([], {
    color: 'red',
    weight: 3
  }).addTo(map);

// 车辆标记
  const busMarker = L.marker([31.2304, 121.4737], {
    icon: busIcon,
    rotationAngle: 0,
    rotationOrigin: 'center'
  }).addTo(map);

// 模拟车辆移动
  setInterval(() => {
    const newLat = busMarker.getLatLng().lat + (Math.random() - 0.5)*0.001;
    const newLng = busMarker.getLatLng().lng + (Math.random() - 0.5)*0.001;

    // 更新位置
    busMarker.setLatLng([newLat, newLng]);

    // 更新方向
    const newAngle = Math.atan2(
      newLng - busMarker.getLatLng().lng,
      newLat - busMarker.getLatLng().lat
    ) * 180 / Math.PI;

    busMarker.setRotationAngle(newAngle);

    // 更新轨迹
    route.addLatLng([newLat, newLng]);
  }, 1000);
});

</script>

<template>
  <div id="map" ref="mapDiv"></div>
</template>
<style scoped>
#map { height: 100vh; width: 100vw; }
</style>

  

posted @ 2025-07-27 16:38  韭菜茄子  阅读(11)  评论(0)    收藏  举报