实现leaflet的轮廓编辑功能

101 阅读1分钟

参考地点云,只实时渲染拖动点与前后点的虚线

<template>
  <div>
    <LPolyline
      ref="polyline"
      @ready="ready"
      :lat-lngs="latlngs"
      :color="color"
    ></LPolyline>
    <!-- 编辑线 -->
    <LPolyline dashArray="8" :lat-lngs="tempLine" />
  </div>
</template>
<script>
import L from "leaflet";
import { LPolyline } from "vue2-leaflet";
import { throttle } from "lodash";
const findDeepMarkerIndex = (arr, marker) => {
  let result;

  const run = (path) => (v, i) => {
    const iRes = path.concat(i);
    if (v._leaflet_id === marker._leaflet_id) {
      result = iRes;
      return true;
    }
    return Array.isArray(v) && v.some(run(iRes));
  };

  arr.some(run([]));
  let returnVal = {};
  if (result) {
    returnVal = {
      indexPath: result,
      index: result[result.length - 1],
      parentPath: result.slice(0, result.length - 1),
    };
  }
  return returnVal;
};

var icon = L.divIcon({
  className: "my-div-icon",
  iconAnchor: [8, 8],
  iconSize: [14, 14],
});

export default {
  components: {
    LPolyline,
  },
  props: {
    latlngs: {},
    map: {},
    color: {
      default: "green",
    },
  },
  data() {
    return {
      markerGroup: null,
      currentIndex: -1,
      currentLatlng: null,
      // 所有顶点
      markers: [],
      // 缓存所有编辑点
      markerCache: [],
      isReady: false,
    };
  },
  computed: {
    tempLine() {
      if (this.currentIndex < 0) return [];
      const len = this.latlngs.length;
      const preIndex = this.currentIndex - 1;
      const nextIndex = this.currentIndex + 1;
      const line = [this.currentLatlng];
      if (preIndex > -1) {
        line.unshift(this.latlngs[preIndex]);
      }
      if (nextIndex < len) {
        line.push(this.latlngs[nextIndex]);
      }
      return line;
    },
  },
  methods: {
    ready() {
      this.initMarkers();
      this.isReady = true;
    },
    initMarkers() {
      const coordsArr = this.$refs.polyline.mapObject.getLatLngs();
      this.markers = coordsArr.map(this.createMarker);
      this.filterMarkerGroup();
      this.map.on("mousemove", this.applyLimitFilters, this);
      // 首次制作时过滤
      this.applyLimitFilters({});
      this.map.addLayer(this.markerGroup);
    },
    createMarker(latlng) {
      const marker = new L.Marker(latlng, {
        draggable: true,
        icon,
      });
      marker._pmTempLayer = true;
      this.markerGroup.addLayer(marker);
      // 监听拖拽事件
      marker.on("move", this.onMarkerDrag, this);
      marker.on("dragend", this.onMarkerDragEnd, this);
      return marker;
    },
    filterMarkerGroup() {
      this.markerCache = [];
      const allMarkers = [...this.markerGroup.getLayers()];
      this.markerCache = allMarkers.filter((v, i, s) => s.indexOf(v) === i);
    },
    applyLimitFilters: throttle(function ({ latlng = { lat: 0, lng: 0 } }) {
      const makersNearCursor = this.filterClosestMarkers(latlng);
      const markersToAdd = [...makersNearCursor];
      this.renderLimits(markersToAdd);
    }, 200),
    filterClosestMarkers(latlng) {
      const markers = [...this.markerCache];
      const limit = 3;
      if (limit === -1) {
        return markers;
      }
      markers.sort((l, t) => {
        const distanceA = l._latlng.distanceTo(latlng);
        const distanceB = t._latlng.distanceTo(latlng);
        return distanceA - distanceB;
      });
      const closest = markers.filter((l, i) => (limit > -1 ? i < limit : true));
      return closest;
    },
    renderLimits(markers) {
      this.markerCache.forEach((l) => {
        if (markers.includes(l)) {
          this.markerGroup.addLayer(l);
        } else {
          this.markerGroup.removeLayer(l);
        }
      });
    },
    onMarkerDrag(e) {
      const { index } = findDeepMarkerIndex(this.markers, e.target);
      this.currentIndex = index;
      this.currentLatlng = [e.latlng.lat, e.latlng.lng];
    },
    onMarkerDragEnd() {
      const preIndex = this.currentIndex;
      this.currentIndex = -1;
      const polyline = JSON.parse(JSON.stringify(this.latlngs));
      polyline[preIndex] = [...this.currentLatlng];
      this.$emit("change", polyline);
    },
  },
  watch: {
    latlngs: {
      immediate: true,
      handler() {
        if (this.markerGroup) {
          this.markerGroup.clearLayers();
        }
        this.markerGroup = new L.FeatureGroup();
        this.markerGroup._pmTempLayer = true;
        if (this.isReady) {
          setTimeout(() => {
            this.initMarkers();
          }, 5);
        }
      },
    },
  },
};
</script>

使用:

image.png