【天地图-点线面最全功能】天地图实现功能:回显、绘制、编辑、删除任意点线面

实现效果图

实现功能

1. 回显点线面数据
2. 绘制点线面,保存可获取点线面数据
3. 编辑点线面,保存可获取最新编辑后的点线面数据
4. 删除任意点线面(解决删除按钮不能随元素位置变化(地图拖拽/放大缩小时)而变化问题->地图放大缩小拖动时处理删除按钮消失)

完整代码:

bash 复制代码
<!DOCTYPE html>
<html>

<head>
  <meta http-equiv="content-type" content="text/html; charset=utf-8" />
  <title>回显、绘制、编辑、删除任意点线面</title>
  <script src="http://api.tianditu.gov.cn/api?v=4.0&amp;tk=应用KEY值"
    type="text/javascript"></script>
  <style type="text/css">
    body,
    html {
      width: 100%;
      height: 100%;
      margin: 0;
      font-family: '微软雅黑';
    }

    #map {
      height: 400px;
      width: 100%;
    }

    input {
      margin-top: 10px;
      margin-left: 5px;
      font-size: 14px;
    }

    #contextMenu {
      position: absolute;
      background: white;
      border: 1px solid #ccc;
      box-shadow: 2px 2px 3px rgba(0, 0, 0, 0.2);
      z-index: 1000;
      display: none;
    }

    .context-menu-item {
      padding: 2px 12px;
      cursor: pointer;
      font-size: 14px;
    }

    .context-menu-item:hover {
      background: #f0f0f0;
    }
  </style>
  <script>
    var map, zoom = 10, handler;
    let isEdit = false; // 是否启用编辑
    // 右键菜单相关变量
    let contextMenu = null;
    let currentFeature = null; // 当前右键点击的元素
    let currentIndex = -1;     // 当前元素的索引
    let featureType = '';      // 元素类型:point/line/polygon

    let viewChangeHandler = null;// 地图视图变化监听器

    // 点数据
    let pointOriginalArr = [
      [120.2536, 30.27093]
    ]
    // 线数据
    let lineOriginalArr = [
      [
        [119.86084, 30.41702],
        [119.96796, 30.47353],
        [119.98718, 30.36758],
      ],
      [
        [119.91714, 30.1855],
        [120.10666, 30.27923],
      ]
    ]
    // 面数据
    let polygonOriginalArr = [
      [
        [119.5752, 30.35392],
        [119.72488, 30.35273],
        [119.65347, 30.26025],
      ],
      [
        [120.49118, 30.29109],
        [120.70541, 30.30532],
        [120.50629, 30.20211],
        [120.69855, 30.22703]
      ]
    ]

    function onLoad () {
      // 创建右键菜单
      createContextMenu();
      //初始化地图对象
      map = new T.Map("map");
      //设置显示地图的中心点和级别
      map.centerAndZoom(new T.LngLat(120.216329, 30.252589), zoom);
      // 绑定地图点击事件隐藏菜单
      map.addEventListener('click', hideContextMenu);

      // 地图视图变化监听
      viewChangeHandler = function () {
        if (currentFeature) {
          updateMenuPosition();
        }
      };
      map.addEventListener('movestart', hideContextMenu);
      map.addEventListener('moveend', viewChangeHandler);
      map.addEventListener('zoomend', viewChangeHandler);


      // 初始显示
      showPoint()
      showPolyLine()
      showPolygon()
    }

    // 位置更新
    function updateMenuPosition () {
      if (!currentFeature) return;

      let centerLngLat;
      switch (featureType) {
        case 'point':
          centerLngLat = currentFeature.getLngLat();
          break;
        case 'line':
          const lineLnglats = currentFeature.getLngLats();
          centerLngLat = getCenter(lineLnglats);
          break;
        case 'polygon':
          const polygonRings = currentFeature.getLngLats();
          centerLngLat = getCenter(polygonRings[0]);
          break;
      }

      const containerPoint = map.lngLatToContainerPoint(centerLngLat);
      const mapRect = map.getContainer().getBoundingClientRect();

      contextMenu.style.left = (containerPoint.x + mapRect.left) + 'px';
      contextMenu.style.top = (containerPoint.y + mapRect.top) + 'px';
    }

    // 几何中心计算方法
    function getCenter (lnglats) {
      // 确保有效的坐标数组
      if (!lnglats || lnglats.length === 0) return null;

      // 计算所有坐标点的平均值
      const sum = lnglats.reduce((acc, curr) => {
        // 处理不同格式的坐标(支持T.LngLat对象和普通数组)
        const lng = curr instanceof T.LngLat ? curr.lng : curr[0];
        const lat = curr instanceof T.LngLat ? curr.lat : curr[1];
        return {
          lng: acc.lng + lng,
          lat: acc.lat + lat
        };
      }, { lng: 0, lat: 0 });

      // 返回新的T.LngLat对象
      return new T.LngLat(
        sum.lng / lnglats.length,
        sum.lat / lnglats.length
      );
    }

    // 创建右键菜单
    function createContextMenu () {
      contextMenu = document.createElement('div');
      contextMenu.id = 'contextMenu';
      contextMenu.innerHTML = `
        <div class="context-menu-item" onclick="deleteFeature()">删除</div>
      `;
      document.body.appendChild(contextMenu);
    }

    // 显示右键菜单
    function showContextMenu (e, feature, index, type) {
      // 获取天地图事件中的原生事件对象
      const originalEvent = e.originalEvent || e.event || e;
      if (originalEvent.preventDefault) {
        originalEvent.preventDefault();
      }

      currentFeature = feature;
      currentIndex = index;
      featureType = type;

      updateMenuPosition(); // 初始定位

      contextMenu.style.display = 'block';
      // 使用原生事件的坐标,定位菜单项
      contextMenu.style.left = originalEvent.clientX + 'px';
      contextMenu.style.top = originalEvent.clientY + 'px';
    }

    // 隐藏右键菜单
    function hideContextMenu () {
      contextMenu.style.display = 'none';
      currentFeature = null;
      currentIndex = -1;
      featureType = '';
    }

    // 删除要素
    function deleteFeature () {
      if (currentIndex === -1) return;

      switch (featureType) {
        case 'point':
          pointOriginalArr.splice(currentIndex, 1);
          break;
        case 'line':
          lineOriginalArr.splice(currentIndex, 1);
          break;
        case 'polygon':
          polygonOriginalArr.splice(currentIndex, 1);
          break;
      }
      refreshMap();
      hideContextMenu();
    }

    // 刷新地图
    function refreshMap () {
      map.clearOverLays();
      showPoint();
      showPolyLine();
      showPolygon();
    }

    // 显示点
    function showPoint () {
      let pointArr = [];
      pointOriginalArr.forEach((item, index) => {
        let arr = []
        arr.push(new T.LngLat(item[0], item[1]))
        pointArr.push(arr);
      })
      pointArr.forEach((item, index) => {
        var marker = new T.Marker(item[0], {
          icon: new T.Icon({
            iconUrl: './imgs/marker-icon.png',
          }),
        });
        map.addOverLay(marker);

        // 右键菜单事件绑定(添加event参数传递)
        marker.addEventListener('contextmenu', function (apiEvent) {
          showContextMenu(apiEvent, marker, index, 'point');
        });

        if (!isEdit) return;
        marker.enableDragging();
        marker.addEventListener('dragend', function (e) {
          updatePointOriginalList([e.lnglat.lng, e.lnglat.lat], index)
        })
      })
    }

    // 更新点的数据,用于最后保存
    function updatePointOriginalList (arr, index) {
      pointOriginalArr[index] = arr
    }

    // 显示线
    function showPolyLine () {
      let lineArr = []
      lineOriginalArr.forEach((item, index) => {
        let arr = []
        item.forEach((item1) => {
          arr.push(new T.LngLat(item1[0], item1[1]))
        })
        lineArr.push(arr)
      })
      lineArr.forEach((item, index) => {
        var line = new T.Polyline(item);
        map.addOverLay(line);

        // 右键菜单事件绑定(添加event参数传递)
        line.addEventListener('contextmenu', function (apiEvent) {
          showContextMenu(apiEvent, line, index, 'line');
        });

        if (!isEdit) return
        line.enableEdit();
        line.addEventListener('edit', function (e) {
          const ht = line.getLngLats()
          let arr = []
          ht.forEach((item) => {
            arr.push([item.lng, item.lat])
          })
          updateLineOriginalList(arr, index)
        })
      })
    }

    // 更新线的数据,用于最后保存
    function updateLineOriginalList (arr, index) {
      lineOriginalArr[index] = arr
    }

    // 显示面
    function showPolygon () {
      let polygonArr = []
      polygonOriginalArr.forEach((item, index) => {
        let arr = []
        item.forEach((item1) => {
          arr.push(new T.LngLat(item1[0], item1[1]))
        })
        polygonArr.push(arr)
      })
      polygonArr.forEach((item, index) => {
        var polygon = new T.Polygon(item);
        map.addOverLay(polygon);

        // 右键菜单事件绑定(添加event参数传递)
        polygon.addEventListener('contextmenu', function (apiEvent) {
          showContextMenu(apiEvent, polygon, index, 'polygon');
        });

        if (!isEdit) return
        polygon.enableEdit();
        polygon.addEventListener('edit', function (e) {
          const ht = polygon.getLngLats()[0]
          let arr = []
          ht.forEach((item) => {
            arr.push([item.lng, item.lat])
          })
          updatePolygonOriginalList(arr, index)
        })
      })
    }

    // 更新面的数据,用于最后保存
    function updatePolygonOriginalList (arr, index) {
      polygonOriginalArr[index] = arr
    }

    // 打点
    function openMarkerTool () {
      if (handler) handler.close();
      handler = new T.MarkTool(map, {
        follow: true,
        icon: new T.Icon({
          iconUrl: './imgs/marker-icon.png',
          // iconSize: new T.Point(24, 28), // 图标可视区域的大小
        }),
      });
      handler.open();
      handler.addEventListener('mouseup', function (e) {
        // 这里可以获取到当前点位的坐标
        pointOriginalArr.push([e.currentLnglat.lng, e.currentLnglat.lat])
        // 重新绘制地图,否则编辑不了
        refreshMap();
        handler.clear()
      })
    }

    // 画线
    function openPolylineTool () {
      if (handler) handler.close();
      handler = new T.PolylineTool(map);
      handler.open();
      handler.addEventListener('draw', function (e) {
        const currentLnglats = e.currentLnglats
        let arr = []
        currentLnglats.forEach((item, index) => {
          arr.push([item.lng, item.lat])
        })
        lineOriginalArr.push(arr)
        // 重新绘制地图,否则编辑不了
        refreshMap();
        handler.clear()
      })
    }

    // 画面
    function openPolygonTool () {
      if (handler) handler.close();
      handler = new T.PolygonTool(map);
      handler.open();
      handler.addEventListener('draw', function (e) {
        const currentLnglats = e.currentLnglats
        let arr = []
        currentLnglats.forEach((item, index) => {
          arr.push([item.lng, item.lat])
        })
        polygonOriginalArr.push(arr)
        // 重新绘制地图,否则编辑不了
        refreshMap();
        handler.clear()
      })
    }

    // 开启编辑
    function editClick () {
      isEdit = true
      map.clearOverLays()
      showPoint()
      showPolyLine()
      showPolygon()
    }
    // 保存
    function saveClick () {
      console.log("点数据:", pointOriginalArr); // 两层结构:大数组中,包含点的小数组
      console.log("线数据:", lineOriginalArr); // 三层结构:大数组中包含线的数组,线的数组中包含经纬度小数组
      console.log("面数据:", polygonOriginalArr); // 三层结构:大数组中包含面的数组,面的数组中包含经纬小数组
      isEdit = false
      map.clearOverLays()
      showPoint()
      showPolyLine()
      showPolygon()

    }
    // 清空画布
    function clearClick () {
      isEdit = false
      map.clearOverLays()
      pointOriginalArr = []
      lineOriginalArr = []
      polygonOriginalArr = []
    }

  </script>
</head>

<body onLoad="onLoad()">
  <div id="map"></div>
  <div class="line-btn">
    <input type="button" value="点工具" onClick="openMarkerTool() " />
    <input type="button" value="线工具" onClick="openPolylineTool() " />
    <input type="button" value="面工具" onClick="openPolygonTool() " />
    <input type="button" value="编辑" onClick="editClick() " />
    <input type="button" value="保存" onClick="saveClick() " />
    <input type="button" value="清除" onClick="clearClick() " />
  </div>
</body>

</html>

复制上面代码,替换应用KEY值即可看到功能效果。

相关推荐
meilindehuzi_a31 分钟前
深入理解 JavaScript 的同步与异步机制:从单线程设计到 Promise 核心应用
开发语言·javascript·ecmascript
如烟花的信页34 分钟前
加速乐cookie逆向分析
javascript·爬虫·python·js逆向
永远的WEB小白39 分钟前
css改变svg图标的颜色
前端·javascript·css
ikoala1 小时前
Codex 不得不装的 12 个插件,都在这了
前端·javascript·后端
赵庆明老师2 小时前
JS检查提交的文件是否合规
开发语言·前端·javascript
颂love2 小时前
Vue的两大生态以及组件通信
前端·javascript·vue.js·typescript
光影少年2 小时前
js单线程,为什在node环境下的js可以处理高并发请求?
前端·javascript·掘金·金石计划
moMo2 小时前
# JavaScript 的“等等我”:聊聊同步与异步
javascript
Cobyte3 小时前
19.Vue Vapor 的实现原理原来这么简单
前端·javascript·vue.js
JackieDYH3 小时前
uniapp vue3 常用的生命周期和作用使用时机
javascript·vue.js·uni-app