天地图判定点位是否在区域内

440 阅读3分钟

主要是使用turf来判定点位,如果是后台生成的点位,天地图推荐使用D3绘制判定,也是支持的,项目中区域绘制不是固定的,就不采用后台方案

<template>
  <div class="map-container">
    <div id="map-canvas"></div>
    <div id="infos">
      <button @click="openPolygonTool">绘制多边形</button>
      <button @click="clearPolygon">清空</button>
    </div>
  </div>
</template>

<script>
/**
 * 人员位置修改,使用轮询或者ws,暴露个setPoint方法,传入经纬度,修改data_info
 * 如果是绘制 polygon,绘制完成也传递经纬度,给后台保存,区域判定 polygon 和 circle目前都可以
 * 目前是一次只能绘制一个区域,看后面要不要改成多个区域判定
 */
import * as turf from "@turf/turf";
export default {
  name: "tdtMap",
  data() {
    return {
      tk: "XXX", // 天地图 key
      data_info: [
        [116.597854, 39.911988, "人员2"],
        [116.406605, 39.921585, "人员3"],
        [116.412222, 39.912345, "人员1"],
      ],
      map: null,
      countries: [],
      polygonTool: null,
      handler: null,
      T: null,
    };
  },
  methods: {
    openPolygonTool() {
      if (this.countries.length) {
        this.countries = [];
        this.map.getOverlays().forEach((item) => {
          // 清除polygon
          if (item.getType() === 5) {
            this.map.removeOverLay(item);
          }
        });
      }
      if (this.handler) this.handler.close();
      this.handler = new this.T.PolygonTool(this.map);
      this.handler.open();
      this.handler.on(
        "draw",
        ({
          type,
          target,
          currentLnglats,
          currentArea,
          currentPolygon,
          allPolygons,
        }) => {
          currentLnglats.forEach((item) => {
            this.countries.push([item.lng, item.lat]);
          });
          this.countries.push([currentLnglats[0].lng, currentLnglats[0].lat]); // 闭合多边形
          this.initMarker();
        }
      );
    },
    clearPolygon() {
      this.countries = [];
      this.map.clearOverLays();
    },
    initMarker() {
      if (this.countries.length) {
        this.data_info.forEach((item) => {
          item[2] = item[2].split(",")[0];
          if (
            this.pointInArea(
              { point: [item[0], item[1]], polygon: [this.countries] },
              "polygon"
            )
          ) {
            item[2] += `,在区域内`;
          } else {
            item[2] += `,不在区域内`;
          }
        });
        this.map.getOverlays().forEach((item) => {
          if (item.getType() === 2) {
            this.map.removeOverLay(item);
          }
        });
        this.initPoint();
      }
    },
    initPoint() {
      for (let i = 0; i < this.data_info.length; i++) {
        let marker = new this.T.Marker(
          new this.T.LngLat(this.data_info[i][0], this.data_info[i][1])
        );
        let content = `${this.data_info[i][2]}`;
        let status = this.data_info[i][2].split(",")[1];
        this.map.addOverLay(marker);
        marker.enableDragging();
        this.addClickHandler(content, marker);
        if (!marker.qQ.drag && !marker.qQ.dragend) {
          marker.on("drag", ({ type, target, lnglat }) =>
            this.markerDrag({ marker, type, target, lnglat, content })
          );
          marker.on("dragend", ({ type, target, lnglat }) =>
            this.markerDragEnd({ marker, type, target, lnglat, content })
          );
        }
      }
    },
    openInfo(content, e) {
      let point = e.lnglat;
      let marker = new this.T.Marker(point);
      let markerInfoWin = new this.T.InfoWindow(content, {
        offset: new this.T.Point(0, -30),
      });
      this.map.openInfoWindow(markerInfoWin, point);
    },
    addClickHandler(content, marker) {
      marker.addEventListener("click", (e) => {
        this.openInfo(content, e);
      });
    },
    markerDrag({ marker, type, target, lnglat, content }) {
      this.map.closeInfoWindow();
    },
    markerDragEnd({ marker, type, target, lnglat, content }) {
      let [user, status] = content.split(",");
      let point = [lnglat.lng, lnglat.lat];
      let text;
      this.data_info.forEach((item) => {
        if (item[2].includes(user)) {
          item[0] = lnglat.lng;
          item[1] = lnglat.lat;
          if (this.countries.length) {
            if (
              this.pointInArea(
                { point: point, polygon: [this.countries] },
                "polygon"
              )
            ) {
              item[2] = `${user},在区域内`;
            } else {
              item[2] = `${user},不在区域内`;
            }
          }
          text = item[2];
        }
      });
      this.addClickHandler(text, marker);
    },
    pointInArea(data, type) {
      if (type === "polygon") {
        let { point, polygon } = data;
        let pt = turf.point(point);
        let poly = turf.polygon(polygon);
        return turf.booleanPointInPolygon(pt, poly);
      } else if (type === "circle") {
        let { point, center, radius } = data;
        return this.pointInCircle(point, center, radius);
      }
    },
    pointInCircle(point, center, radius) {
      const distance = turf.distance(turf.point(point), turf.point(center), {
        units: "metres",
      });
      return distance <= radius;
    },
  },
  mounted() {
    window.T ? (window.T = null) : null;
    const script = document.createElement("script");
    script.src = `http://api.tianditu.gov.cn/api?v=4.0&tk=${this.tk}`;
    script.type = "text/javascript";
    script.onload = () => {
      // 此处可以在外部库加载完成后执行一些初始化操作
      this.T = window.T;
      this.map = new this.T.Map("map-canvas");
      this.map.centerAndZoom(new this.T.LngLat(116.599, 39.9539), 12);
      this.initPoint();
    };
    // 将 script 标签添加到页面
    document.head.appendChild(script);
  },
};
</script>
<style scoped>
.map-container {
  width: 100%;
  height: 100%;
  position: relative;
}
#map-canvas {
  width: 100%;
  height: 100%;
  fill: #000000;
}

#infos {
  position: fixed;
  top: 10px;
  left: 10px;
  z-index: 999;
  background: #fff;
  padding: 10px;
  border-radius: 5px;
  box-shadow: 0 0 5px #ccc;
}
</style>

这里是原生实现,注释对应的代码块实现支持圆形和POLYGON路径区域判定,实际上还是POLYGON,大差不大

<!DOCTYPE html>

<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=Edge">
    <title>天地图-地图API-范例-加载geojson数据</title>
    <link href="../../../../images/favicon.ico" type="image/x-icon" rel="Shortcut Icon" />
    <style type="text/css">
        html {
            height: 100%
        }

        body {
            height: 100%;
            margin: 0;
            padding: 0
        }

        #map-canvas {
            height: 100%;
            fill: #000000;
        }

        /* .geojson { 
            pointer-events: auto !important;
        } */
        #infos {
            position: fixed;
            top: 10px;
            left: 10px;
            z-index: 999;
            background: #fff;
            padding: 10px;
            border-radius: 5px;
            box-shadow: 0 0 5px #ccc;
        }
    </style>
</head>

<body>
    <div id="map-canvas"></div>
    <div id="infos">
        <button id="btnPolygon">画图</button>
    </div>
    <script src=" http://api.tianditu.gov.cn/api?v=4.0&tk=XXX"
        type="text/javascript"></script>
    <script src="http://cdn.bootcss.com/d3/3.5.17/d3.js " charset="utf-8"></script>
    <script src="http://lbs.tianditu.gov.cn/api/js4.0/opensource/openlibrary/D3SvgOverlay.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/@turf/turf@6.3.0/turf.min.js"></script>
    <script>
        let data_info = [[116.597854, 39.911988, "人员2"],
        [116.406605, 39.921585, "人员3"],
        [116.412222, 39.912345, "人员1"]];
        let map = new T.Map("map-canvas");
        map.centerAndZoom(new T.LngLat(116.499, 39.9539), 10)
        let countries = [];



        // CIRCLE
        let circleLnglat = new T.LngLat(116.40769, 39.89945)
        let circle = new T.Circle(circleLnglat, 2000, { color: "blue", weight: .1, opacity: 0.5, fillColor: "#000", fillOpacity: 0.5, });
        map.addOverLay(circle);
        function init() {
            let radius = circle.getRadius()
            let center = [116.40769, 39.89945]

            // console.log(circleLnglat)
            data_info.forEach(item => {
                if (pointInArea({ point: [item[0], item[1]], center, radius }, 'circle')) {
                    item[2] += `,在区域内`
                } else {
                    item[2] += `,不在区域内`
                }
            })
            initPoint()
        }
        init()
        // PATH || POLYGON
        // let countriesOverlay = new T.D3Overlay(init, redraw);
        // d3.json("./data.json", function (data) {
        //     countries = data.features;
        //     data_info.forEach(item => {
        //         if (pointInArea({ point: [item[0], item[1]], polygon: countries[0].geometry.coordinates }, 'polygon')) {
        //             item[2] += `,在区域内`
        //         } else {
        //             item[2] += `,不在区域内`
        //         }
        //     })
        //     initPoint()
        //     map.addOverLay(countriesOverlay)
        //     countriesOverlay.bringToBack();
        // });
        // function init(sel, transform) { // 初始化样式
        //     let upd = sel.selectAll('path.geojson').data(countries);
        //     upd.enter()
        //         .append('path')
        //         .attr("class", "geojson")
        //         .attr('stroke', 'black')
        //         .attr('fill', function (d, i) {
        //             return d3.hsl('red')
        //         })
        //         .attr('fill-opacity', '0.3')
        //         .attr('cursor', 'pointer')
        // }
        // function redraw(sel, transform) { // 给区域设置路径 、绘制
        //     sel.selectAll('path.geojson').each(
        //         function (d, i) {
        //             d3.select(this).attr('d', transform.pathFromGeojson)
        //                 .on('click', function (d, i) {
        //                     // console.log(arguments)
        //                 })
        //         }
        //     )
        // }
        // end
        function initPoint() {
            for (var i = 0; i < data_info.length; i++) {
                let marker = new T.Marker(new T.LngLat(data_info[i][0], data_info[i][1]));  // 创建标注
                let content = `${data_info[i][2]}`; // 创建标注
                let status = data_info[i][2].split(',')[1]
                map.addOverLay(marker);               // 将标注添加到地图中
                marker.enableDragging();              // 可拖拽
                addClickHandler(content, marker);
                if (!marker.qQ.drag && !marker.qQ.dragend) {
                    marker.on('drag', ({ type, target, lnglat }) => markerDrag({ marker, type, target, lnglat, content }))
                    marker.on('dragend', ({ type, target, lnglat }) => markerDragEnd({ marker, type, target, lnglat, content }))
                }
            }
        }
        function openInfo(content, e) {
            let point = e.lnglat;
            marker = new T.Marker(point);// 创建标注
            let markerInfoWin = new T.InfoWindow(content, { offset: new T.Point(0, -30) }); // 创建信息窗口对象
            map.openInfoWindow(markerInfoWin, point); //开启信息窗口
        }
        function addClickHandler(content, marker) {
            marker.addEventListener("click", function (e) {
                openInfo(content, e)
            });
        }

        function markerDrag({ marker, type, target, lnglat, content }) {
            map.closeInfoWindow();
        }
        function markerDragEnd({ marker, type, target, lnglat, content }) {
            let [user, status] = content.split(',')
            let point = [lnglat.lng, lnglat.lat]
            let text;
            // CIRCLE
            data_info.forEach(item => {
                if (item[2].includes(user)) {
                    item[0] = lnglat.lng
                    item[1] = lnglat.lat
                    if (pointInArea({ point: point, center: [116.40769, 39.89945], radius: circle.getRadius() }, 'circle')) {
                        item[2] = `${user},在区域内`
                    } else {
                        item[2] = `${user},不在区域内`
                    }
                    text = item[2]
                }
            })
            // PATH || POLYGON
            // data_info.forEach(item => {
            //     if (item[2].includes(user)) {
            //         item[0] = lnglat.lng
            //         item[1] = lnglat.lat
            //         if (pointInArea({ point: point, polygon: countries[0].geometry.coordinates }, 'polygon')) {
            //             item[2] = `${user},在区域内`
            //         } else {
            //             item[2] = `${user},不在区域内`
            //         }
            //         text = item[2]
            //     }
            // })
            addClickHandler(text, marker);
        }
        // 判断点是否在区域内
        function pointInArea(data, type) {
            if (type === 'polygon') {
                let { point, polygon } = data
                let pt = turf.point(point);
                let poly = turf.polygon(polygon);
                return turf.booleanPointInPolygon(pt, poly);
            } else if (type === 'circle') {
                let { point, center, radius } = data
                return pointInCircle(point, center, radius)
            }
        }
        // 判断是否在圆内
        function pointInCircle(point, center, radius) {
            const distance = turf.distance(turf.point(point), turf.point(center), { units: 'metres' });
            return distance <= radius;
        }
    </script>

</body>

</html>

数据: image.png